国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C多線程、鎖、同步、信號量

這篇具有很好參考價值的文章主要介紹了C多線程、鎖、同步、信號量。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一 線程函數(shù)

1.1 創(chuàng)建線程

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

thread 是線程變量地址
attr是線程屬性,一般為NULL
start_rount 是函數(shù)指針
arg 是函數(shù)指針指向函數(shù)的參數(shù)

1.2 線程退出

void pthread_exit(void *retval);

retval可以把退出值帶回去,例子見線程回收

1.3 線程回收

int pthread_join(pthread_t thread, void **retval);
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

struct Test
{
    int num;
    int age;
};

void* func(void* arg)
{
    struct Test *temp = (struct Test*)arg;

    for(int i = 0 ;i < 5; i++)
    {
        printf("in_sonthread i = %d\n",i);
    }

    temp->age = 12;
    temp->num = 21;

    pthread_exit(temp);
    return NULL;
}



int main()
{
    pthread_t pid;
    struct Test jxk;
     struct Test* jxktemp = &jxk;

    void* ans;

    pthread_create(&pid,NULL,func,&jxk);
    for(int i = 0; i < 5 ;i++)
    {
        printf("in_main,i = %d\n",i);
    }

    pthread_join(pid,&ans);
    struct Test* jxk2 = (struct Test*)ans;
    printf("num is %d,age is %d\n",jxk2->num,jxk2->age);


    return 0;
}   

C多線程、鎖、同步、信號量

1.4 線程分離:

某些情況下,程序的主線程有自己的其他業(yè)務(wù),如果讓主線程負(fù)責(zé)子線程的資源回收,調(diào)用pthrad_join()只要子線程不退出,主線程就會一致阻塞,主線程的任務(wù)也不能執(zhí)行了。

線程庫提供了線程分離函數(shù) pthread_detach(),調(diào)用這個函數(shù)后指定的子線程可以和主線程分離,當(dāng)子線程退出是,其占用的內(nèi)核資源就被操作系統(tǒng)的其他進程接管并回收了。線程分離后,在主線程中使用pthread_join() 就會收不到子線程資源了。

int pthread_detach(pthread_t thread);
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

struct Test
{
    int num;
    int age;
};

void* func(void* arg)
{
    struct Test *temp = (struct Test*)arg;

    for(int i = 0 ;i < 5; i++)
    {
        printf("in_sonthread i = %d\n",i);
    }

    temp->age = 12;
    temp->num = 21;
    
    printf("子線程:%ld\n",pthread_self());
    pthread_exit(temp);
    return NULL;
}



int main()
{
    pthread_t pid;
    struct Test jxk;
    pthread_create(&pid,NULL,func,&jxk);
    printf("主線程:%ld\n",pthread_self());

    pthread_detach(pid);
    pthread_exit(NULL);

    return 0;
}   

主線程打印了自己的id后便退出了線程,子線程會繼續(xù)運行
C多線程、鎖、同步、信號量

1.5 其他線程函數(shù)

1.5.1 線程取消

線程取消就是在某些特定情況下,在一個線程中殺死另一個線程。使用這個函數(shù)殺死一個線程需要分兩步:

  1. 在線程A中調(diào)用線程取消函數(shù) pthread_cancel,指定殺死線程B,這時候線程B是死不了的
  2. 在線程B中進行一個系統(tǒng)調(diào)用(從用戶區(qū)切換到內(nèi)核區(qū)),否則線程B可以一直運行。
int pthread_cancel(pthread_t thread);
1.5.2 線程ID比較

在Linux中,線程ID本質(zhì)就是一個無符號長整型,因此可以直接使用比較操作符比較兩個線程的ID。但是線程庫是可以跨平臺使用的,在某些平臺上 pthread_t 可能不是一個單純的整型,這種情況下比較兩個線程的ID必須要使用線程比較函數(shù)。

 int pthread_equal(pthread_t t1, pthread_t t2);

二 線程同步

假設(shè)有4個線程ABCD,當(dāng)前一個線程A對內(nèi)存中的共享資源進行訪問的時候,其他線程BCD都不可以對這塊內(nèi)存進行操作,直到線程A對這塊內(nèi)存訪問完畢為止,BCD中的一個才能訪問這塊內(nèi)存,剩下的兩個需要繼續(xù)阻塞等待,以此類推,直到所有的線程都完成對這塊內(nèi)存的操作。

線程內(nèi)對這塊內(nèi)存的訪問方式就稱之為線程同步。所謂線程同步不是說多個線程同時對內(nèi)存進行訪問,而是按照先后順序依次進行的。

例子:兩個線程數(shù)數(shù)字

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

int Number;

void* func1(void* arg)
{
    for(int i = 0; i < 50; i++)
    {
        int cur = Number;
        cur++;
        usleep(100);
        Number = cur;
        printf("thread A %ld, number is %d\n",pthread_self(),Number);
        
    }
    
    return NULL;
}

void* func2(void* arg)
{
    for(int i = 0; i < 50; i++)
    {
        int cur = Number;
        cur++;
        Number = cur;
        usleep(50);
        printf("thread B %ld, number is %d\n",pthread_self(),Number);
    }

    return NULL;
}




int main()
{
    pthread_t pidA,pidB;
    pthread_create(&pidA,NULL,func1,NULL);
    pthread_create(&pidB,NULL,func2,NULL);
    printf("main thread %ld\n",pthread_self());
    sleep(1);
}   

C多線程、鎖、同步、信號量

2.1 互斥鎖

2.1.1定義
pthread_mutex_t mutex;
2.1.2 初始化
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);
//attr 是鎖屬性,一般為NULL

restrict 是一個關(guān)鍵字,用來修飾指針,只有這個關(guān)鍵字修飾的指針可以訪問指向的內(nèi)存地址,其他指針都不行。
// p = mutex, p也不能訪問mutex的地址

2.1.3 銷毀
int pthread_mutex_destroy(pthread_mutex_t *mutex);
2.1.4 加鎖 、 常試鎖、解鎖
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//不會阻塞,會返回一個錯誤號

int pthread_mutex_unlock(pthread_mutex_t *mutex);
2.1.5 互斥鎖使用

解決上訴數(shù)數(shù)問題:

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>

int Number;
pthread_mutex_t numMutex;

void* func1(void* arg)
{
    for(int i = 0; i < 50; i++)
    {
        pthread_mutex_lock(&numMutex);
        int cur = Number;
        cur++;
        Number = cur;
        printf("thread A %ld, number is %d\n",pthread_self(),Number);
        pthread_mutex_unlock(&numMutex);
        usleep(100);
        
    }
    
    return NULL;
}

void* func2(void* arg)
{
    for(int i = 0; i < 50; i++)
    {
        pthread_mutex_lock(&numMutex);
        int cur = Number;
        cur++;
        Number = cur;
        printf("thread B %ld, number is %d\n",pthread_self(),Number);
        pthread_mutex_unlock(&numMutex);
        usleep(50);
    }

    return NULL;
}




int main()
{
    pthread_t pidA,pidB;
    if(pthread_mutex_init(&numMutex,NULL) != 0)
    {
        fprintf(stderr,"init mutex lock failed;\n");
        return 0;
    }
    pthread_create(&pidA,NULL,func1,NULL);
    pthread_create(&pidB,NULL,func2,NULL);
    printf("main thread %ld\n",pthread_self());
    
    pthread_join(pidA,NULL);
    pthread_join(pidB,NULL);
    pthread_mutex_destroy(&numMutex);
}   

C多線程、鎖、同步、信號量

2.2 死鎖

造成死鎖的場景:
多個線程訪問共享資源時,需要加鎖,如果鎖使用不當(dāng),就會造成死鎖這種現(xiàn)象。如果線程死鎖造成的后果是:所有的線程被阻塞,并且進行的阻塞是無法解開的(能解開就不會被阻塞了);

  • 加鎖之后忘記解鎖
  • 加鎖之后,在解鎖之前,程序由其他出口跳出當(dāng)前函數(shù)邏輯(異常、滿足條件的return等)
  • 重復(fù)加鎖,造成死鎖

2.3 如何避免死鎖

  • 避免多次鎖定,多檢查
  • 對共享資源訪問完畢之后,一定要解鎖,或者在加鎖的時候先使用trylock
  • 如果程序中有多把鎖,可以控制對鎖的訪問順序(順序訪問資源、但在有些情況下做不到),另外也可以在對其他互斥鎖做加鎖操作之前,先釋放當(dāng)前線程擁有的互斥鎖。
  • 項目程序可以引入一些專門用于死鎖檢測的模塊。

2.4 讀寫鎖

2.4.1 基本信息(特點和記錄信息)

讀寫鎖是互斥鎖的升級版,在做讀操作的時候可以提高程序的執(zhí)行效率,如果所有的線程都是做讀操作,那么讀是并行的,但是使用互斥鎖,讀操作也是串行的。

**讀寫鎖是一把鎖。**類型是 pthrad_rwlock_t,有了類型就可以創(chuàng)建一把互斥鎖了。

pthread_rwlock_t rwlock;

這把鎖記錄了這些信息:

  • 鎖的狀態(tài):鎖定/打開
  • 鎖定的是什么操作:讀操作/寫操作,使用讀寫鎖鎖定讀操作,需要先解鎖才能去鎖定寫操作,反之亦然
  • 哪個線程把這把鎖鎖上了。

讀寫鎖的特點:

  • 使用讀寫鎖的讀鎖鎖定了臨界區(qū),線程對臨界區(qū)的訪問是并行的,讀鎖是共享的。
  • 使用讀寫鎖的寫鎖鎖定了臨界區(qū),線程對臨界區(qū)的訪問是串行的,寫鎖是獨占的。
  • 使用讀寫鎖分別對兩個臨界區(qū)加了讀鎖和寫鎖,兩個線程要同時訪問兩個臨界區(qū),訪問寫鎖臨界區(qū)的線程繼續(xù)運行,訪問讀鎖的臨界區(qū)線程阻塞,因為寫鎖的有點急比讀鎖高。
2.4.2 類型定義 、 初始化函數(shù) 和 銷毀函數(shù)
//類型
pthread_rwlock_t rwlock;

//初始化
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
           const pthread_rwlockattr_t *restrict attr);
//rwlock,讀寫鎖地址
//attr 讀寫鎖屬性,一般為NULL

//銷毀
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

2.4.3 讀鎖 和 嘗試讀函數(shù)
//在程序中進行讀操作
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
//調(diào)用這個函數(shù),如果讀寫鎖是打開的,那么加鎖成功;
//如果讀寫鎖已經(jīng)鎖定了讀操作,依然可以加鎖成功,因為讀鎖是共享的;
//如果讀寫鎖已經(jīng)鎖定了寫操作,調(diào)用這個函數(shù)會被阻塞。

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
//調(diào)用這個函數(shù),如果讀寫鎖是打開的,那么加鎖成功;
//如果讀寫鎖已經(jīng)鎖定了讀操作,依然可以加鎖成功,因為讀鎖是共享的;
//如果讀寫鎖已經(jīng)鎖定了寫操作,調(diào)用這個函數(shù)加鎖失敗,但是線程不會被阻塞,可以在程序中對函數(shù)返回值進行判斷,添加加鎖失敗后的處理動作。
2.4.4 寫鎖
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
//調(diào)用者函數(shù),如果讀寫鎖是打開的,那么加鎖成功;
//如果已經(jīng)鎖定了讀操作 或 寫操作,調(diào)用這個函數(shù)線程會阻塞。
2.4.5 解鎖
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
2.4.6 讀寫鎖的使用

例:8個線程同時操作一個全局變量,三個線程不定時寫資源,5個線程不定時讀資源。

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>

int Number;
pthread_rwlock_t rwLock;

void* readNum(void* arg)
{
    for(int i = 0; i < 50; i++)
    {
        pthread_rwlock_rdlock(&rwLock);
        printf("Thread read, id = %ld, number = %d\n",pthread_self(),Number);
        pthread_rwlock_unlock(&rwLock);
        usleep(rand() % 5);
    }
    
    return NULL;
}

void* writeNum(void* arg)
{
    for(int i = 0; i < 50; i++)
    {
        pthread_rwlock_wrlock(&rwLock);
        int cur = Number;
        cur++;
        Number = cur;
         printf("Thread write, id = %ld, number = %d\n",pthread_self(),Number);
        pthread_rwlock_unlock(&rwLock);
        usleep(5);
    }

    return NULL;
}




int main()
{
    pthread_t pidA[5],pidB[3];
    if(pthread_rwlock_init(&rwLock,NULL) != 0)
    {
        fprintf(stderr,"init mutex lock failed;\n");
        return 0;
    }

    for(int i = 0; i < 5; i++)
    {
        pthread_create(&pidA[i],NULL,readNum,NULL);
    }

    for(int i = 0; i < 3; i++)
    {
        pthread_create(&pidB[i],NULL,writeNum,NULL);
    }

    //阻塞,資源回收
    for(int i = 0; i < 5; i++)
    {
        pthread_join(pidA[i],NULL);
    }

    for(int i = 0; i < 3; i++)
    {
        pthread_join(pidB[i],NULL);
    }

    pthread_rwlock_destroy(&rwLock);

    printf("main thread %ld\n",pthread_self());

    return 0;
}   

C多線程、鎖、同步、信號量

2.5 條件變量

嚴(yán)格意義上說,條件變量的主要作用不是用來處理線程同步,而是進行線程的阻塞。

2.5.1 數(shù)據(jù)類型、初始化 和 釋放
pthread_cond_t cond;

int pthread_cond_init(pthread_cond_t *restrict cond,
           const pthread_condattr_t *restrict attr);
//cond 條件變量地址
//attr 條件變量屬性,一般為NULL   
        
int pthread_cond_destroy(pthread_cond_t *cond);
       
2.5.2 wait 和 timewait
int pthread_cond_wait(pthread_cond_t *restrict cond,
    pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex,
           const struct timespec *restrict abstime);
// 只會阻塞一定的時間長度,時間過了之后就會繼續(xù)往下執(zhí)行

time_t mytim = time(NULL);
struct timespec tmsp;
tmsp.tv_nsec = 0;
tmsp.tv_sec = tume(NULL) + 100; //線程阻塞100s

2.5.3 signal 和 broadcast
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

調(diào)用以上兩個函數(shù)的任意一個,都可以喚醒被 pthread_cond_wait 或者 pthread_cond_timewait 阻塞的線程;
區(qū)別在于:

  • pthread_cond_signal 是喚醒最少一個被阻塞的線程(總個數(shù)不定);
  • pthread_cond_broadcast是喚醒所有被阻塞的線程。

2.6 信號量

信號量用在多線程多任務(wù)同步的,一個線程完成了某一動作就通過信號量告訴別的線程,別的線程再進行某些動作。信號量不一定是鎖定某一資源,而是流程上的概念,比如:有AB兩個線程,B線程要等A線程完成某一個任務(wù)后在進行自己下面的步驟,這個任務(wù)并不一定是鎖定某一資源,還可以是進行一些計算或者數(shù)據(jù)處理之類。

信號量和條件變量一樣用于處理生產(chǎn)者和消費者模型,用于阻塞生產(chǎn)者線程或消費者線程的運行,
類型為 sem_t ,對應(yīng)頭文件是 <semaphore.h>

#include<semaphore.h>
sem_t sem;
2.6.1 類型定義、初始化 和 銷毀
//類型定義
sem_t sem;

//初始化
int sem_init(sem_t *sem, int pshared, unsigned int value);
// sem:信號量變量地址
// pshared:
//      0:線程同步
//      非0:進程同步
//value: 初始化當(dāng)前信號量擁有的資源(>=0,如果資源數(shù)為0,線程就會被阻塞)

//銷毀
int sem_destroy(sem_t *sem);

2.6.2 sem_wai 和 sem_trywait
//調(diào)用函數(shù)嗲用sem中的資源樹就會消耗一個,資源數(shù) -1
int sem_wait(sem_t* sem);

當(dāng)調(diào)用這個函數(shù),并且sem中的資源數(shù)>0,線程不會阻塞,線程會占用sem中的一個資源,因此資源數(shù)-1,直到sem中的資源減為0時,資源被耗盡,因此線程也就阻塞了。

//調(diào)用函數(shù)嗲用sem中的資源樹就會消耗一個,資源數(shù) -1
int sem_trywait(sem_t* sem);

當(dāng)調(diào)用這個函數(shù),并且sem中的資源數(shù)>0,線程不會阻塞,線程會占用sem中的一個資源,因此資源數(shù)-1,直到sem中的資源減為0時,資源被耗盡,線程不會阻塞,直接返回錯誤,因此可以在程序中添加判斷分支,用于處理獲取資源失敗之后的情況。

2.6.3 sem_timedwait
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

struct timespec {
               time_t tv_sec;      /* Seconds */
               long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */
           };
2.6.4 sem_getvalue
int sem_getvalue(sem_t *sem, int *sval);


2.6.5 sem_post
int sem_post(sem_t *sem);
2.6.6 使用

例子1:
一個空位,不會出現(xiàn)問題,但是當(dāng)生產(chǎn)著剛開始給的value大時會出問題,因為訪問了公共資源。

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<semaphore.h>

struct Node
{
    int number;
    struct Node* next;
};

struct Node* head = NULL;

sem_t semp,semc;

void* consumerFunc(void *)
{
    while(1)
    {
        //查看是否可以消費
        sem_wait(&semc);

        //消費
        struct Node* node = head;
        printf("消費者,id : %ld, number:%d\n",pthread_self(),node->number);
        head = head->next;
        free(node);

        //消費結(jié)束,提示生產(chǎn)者,繼續(xù)生產(chǎn)
        sem_post(&semp);

        sleep(rand() % 3);

    }


}

void* producerFuc(void *)
{
    while(1)
    {
        //看是否可以生產(chǎn)
        sem_wait(&semp);

        //生產(chǎn)資源
        struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
        newNode->number = rand() % 1000;
        newNode->next = head;
        head = newNode;
        printf("生產(chǎn)者,id: %ld,number: %d\n",pthread_self(),newNode->number);

        //提示消費者可以消費了
        sem_post(&semc);
        sleep(rand() % 3);
    }

    return NULL;
}

int main()
{
    pthread_t consumer[5],producer[5];

    //初始化信號量,只有一個空位供給生產(chǎn)消費
    sem_init(&semp,0,1);
    sem_init(&semc,0,0);

    //創(chuàng)建線程
    for(int i = 0; i < 5; i++)
    {
        pthread_create(&consumer[i],NULL,producerFuc,NULL);
    }

    for(int i = 0; i < 5; i++)
    {
        pthread_create(&producer[i],NULL,consumerFunc,NULL);
    }

    //阻塞回收
    for(int i = 0; i < 5; i++)
    {
        pthread_join(consumer[i],NULL);
    }

    for(int i = 0; i < 5; i++)
    {
        pthread_join(producer[i],NULL);
    }

    //信號量釋放
    sem_destroy(&semp);
    sem_destroy(&semc);

    return 0;
}   

加鎖解決問題:

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<semaphore.h>

struct Node
{
    int number;
    struct Node* next;
};

struct Node* head = NULL;

sem_t semp,semc;
pthread_mutex_t mutex;

void* consumerFunc(void *)
{
    while(1)
    {
        //查看是否可以消費
        sem_wait(&semc);
        pthread_mutex_lock(&mutex);
        //加在 sem_wait下,避免死鎖

        //消費
        struct Node* node = head;
        printf("消費者,id : %ld, number:%d\n",pthread_self(),node->number);
        head = head->next;
        free(node);

        pthread_mutex_unlock(&mutex);
        //消費結(jié)束,提示生產(chǎn)者,繼續(xù)生產(chǎn)
        sem_post(&semp);

        sleep(rand() % 3);

    }


}

void* producerFuc(void *)
{
    while(1)
    {
        //看是否可以生產(chǎn)
        sem_wait(&semp);
        pthread_mutex_lock(&mutex);
        //生產(chǎn)資源
        struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
        newNode->number = rand() % 1000;
        newNode->next = head;
        head = newNode;
        printf("生產(chǎn)者,id: %ld,number: %d\n",pthread_self(),newNode->number);

        pthread_mutex_unlock(&mutex);
        //提示消費者可以消費了
        sem_post(&semc);
        sleep(rand() % 3);
    }

    return NULL;
}

int main()
{
    pthread_t consumer[5],producer[5];

    //初始化信號量,只有一個空位供給生產(chǎn)消費
    sem_init(&semp,0,1);
    sem_init(&semc,0,0);

    //初始化mutex
    pthread_mutex_init(&mutex,NULL);

    //創(chuàng)建線程
    for(int i = 0; i < 5; i++)
    {
        pthread_create(&consumer[i],NULL,producerFuc,NULL);
    }

    for(int i = 0; i < 5; i++)
    {
        pthread_create(&producer[i],NULL,consumerFunc,NULL);
    }

    //阻塞回收
    for(int i = 0; i < 5; i++)
    {
        pthread_join(consumer[i],NULL);
    }

    for(int i = 0; i < 5; i++)
    {
        pthread_join(producer[i],NULL);
    }

    //信號量釋放
    sem_destroy(&semp);
    sem_destroy(&semc);

    pthread_mutex_destroy(&mutex);
    return 0;
}   

加鎖是加在了sem_wait下,這樣可以避免死鎖,不然會出現(xiàn)一種情況:
A線程拿到了sem的資源,但是B線程先鎖定阻塞在了mutex位置,就會導(dǎo)致最后生產(chǎn)著無法生產(chǎn),消費者無法消費,死鎖了。文章來源地址http://www.zghlxwxcb.cn/news/detail-432106.html

到了這里,關(guān)于C多線程、鎖、同步、信號量的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包