Liunx線程的同步

什麼是線程間的同步:
對於可以處理同一公共資源的線程來說他們必須是互斥的,這樣才能保證數據的正確性。可是就這一種關係仍然不能處理好這些線程之間的問題。
比如,兩個線程可以訪問同一塊緩衝區,線程1可以往裏面寫數據,線程2可以從中讀數據,要是數據遠遠大於緩衝區的大小時,這時當線程1寫滿緩衝區時,就不能在寫入了,必須等到線程2去讀掉緩衝區的數據之後才能在寫入。所以最好的做法就是在線程1寫滿時,去通知線程2要去讀緩衝區的數據了,這就是線程之間的同步。

可以發現,線程間同步的基礎是線程之間是互斥的,要是線程之間沒有互斥關係也就談不上同步了。

要了解現線程之間的同步關係,我們以簡單的生產者消費者模型進行討論。
對生產者和消費者的關係進行分析:
生產者與生產者:互斥關係,同時只有一個生產者可以生產數據,不然會發生數據的異常。
生產者與消費者:互斥與同步關係,生產者生產時消費者不能去讀取數據,不然會發生數據異常;當生產者將緩衝區生產滿時或者其他條件必須讓消費者讀取時,需要通知消費者去讀取數據。
消費者與消費者:互斥關係,對於一個數據,只能被一個消費者所讀取。
在此我們以一個生產者與一個消費者爲例。

同步機制的實現條件:
①互斥是同步的基礎,所以要實現同步必須先實現線程間的互斥。
②引入條件變量,條件變量是一種數據,用來描述資源就緒與否的狀態,它與互斥機制結合使用就可實現同步機制。

條件變量的操作:
①條件變量的初始化:
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
參數描述:
cond:要初始化的條件變量,是一個pthread_cond_t類型的變量。
attr:條件變量的狀態,初始化時默認爲NULL
返回值:
成功返回0,失敗返回錯誤碼
②條件變量的銷燬
int pthread_cond_destroy(pthread_cond_t *cond);
參數描述
cond:要銷燬的條件變量。
返回值:
成功返回0,失敗返回錯誤碼。
③條件變量的等待
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
函數說明:在條件不滿足時會被掛起到等待隊列,此時線程會釋放掉鎖資源,在線程被喚醒退出等待隊列時,會重新獲得鎖資源。
參數描述:
cond:條件變量
mutex:互斥鎖
④條件變量的喚醒
int pthread_cond_signal(pthread_cond_t *cond);
函數說明:在一個線程需要喚醒另一個等待的線程時使用,cond是這兩個線程共同使用的條件變量。
參數說明:
cond:標識那個條件變量
返回值:
成功返回0,失敗返回錯誤碼。
int pthread_cond_broadcast(pthread_cond_t *cond);
函數說明:與上面的函數類似,不過上面的是激活一個線程,這個是激活所有等待的線程

【生產者與消費者模型】
緩衝區是一個單鏈表,生產者生成結點插入到單鏈表的頭部,消費者從單鏈表的頭部拿到結點。規定生產者每次生產一個結點消費者就去拿到這個結點。
分析:
①在單鏈表爲空時,消費者不能去拿結點,需要等待生產者生產結點。
②在生產者生產時,消費者不能去拿結點。
③當生產者生產完成一個結點後需要告訴消費者可以拿結點了,這就需要使用條件變量。
④消費者拿結點時,生產者不能生產結點。這就需要互斥鎖用來實現它們之間的互斥。

代碼實現:

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

typedef struct Node{
    int value;
    struct Node* next;
}Node;

Node* alloc(int value)
{
    Node* pTmp = (Node*) malloc(sizeof(Node));
    pTmp->value = value;
    pTmp->next = NULL;
    return pTmp;
}
void Free(Node* pTmp)
{
    if(pTmp)
        free(pTmp);
    pTmp = NULL;
}

void InitList(Node** pHead)
{
    *pHead = (Node*) malloc(sizeof(Node));
    (*pHead)->next = NULL;
}

void PushFront(Node* pHead, int value)
{
    Node* pTmp = alloc(value);
    pTmp->next = pHead->next;
    pHead->next = pTmp;
}

void PopFront(Node* pHead)
{
    Node* pTmp = pHead->next;
    if(pTmp)
    {
    pHead->next = pTmp->next;
        Free(pTmp);
    }
}
void DestroyList(Node** pHead)
{
    while((*pHead)->next)
    {
    PopFront(*pHead);
    }
    Free(*pHead);
}
void PrintList(Node* pHead)
{
    while(pHead->next)
    {
    printf("%d ", pHead->next->value);
    pHead = pHead->next;
    }
    printf("\n");
}
int isEmpty(Node* pHead)
{
    if(pHead->next)
    return 0;
    return 1;
}   
int SizeList(Node* pHead)
{
    int count = 0;
    pHead = pHead->next;
    while(pHead)
    {
    count++;
    pHead = pHead->next;
    }
    return count;
}
//創建鏈表
Node* pHead;
//創建條件變量
pthread_cond_t need_prod = PTHREAD_COND_INITIALIZER;
//創建互斥鎖
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

//生產者
void *product(void *arg)
{
    while(1){
    sleep(1);
        //申請鎖
        pthread_mutex_lock(&lock);
        int data = rand() % 123;
        PushFront(pHead, data);
        printf("生產者生產完成: %d\n", data); 
        //釋放鎖
        pthread_mutex_unlock(&lock);
        //喚醒消費者線程
        pthread_cond_signal(&need_prod);
    }
}
//消費者
void *consumer(void *arg)
{
    while(1){
    //申請鎖
        pthread_mutex_lock(&lock);
        while(isEmpty(pHead))
        {
        printf("生產者沒有生產完畢,消費者等待\n");
        //如果鏈表中爲空,消費者就要進行等待,等生產者生產完成後喚醒自己
        //在進入等待隊列時,它會釋放鎖資源,以便其他進程進行生產,在出等待隊列時,它會恢復鎖的狀態
        pthread_cond_wait(&need_prod, &lock);
        }
        printf("消費者消費完畢: %d\n", pHead->next->value);
        PopFront(pHead);
        //釋放鎖
        pthread_mutex_unlock(&lock);
    }
}
int main()
{
    InitList(&pHead);
    pthread_t thread1;
    pthread_t thread2;
    pthread_create(&thread1, NULL, product, NULL);
    pthread_create(&thread2, NULL, consumer, NULL);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    //銷燬互斥鎖
    pthread_mutex_destroy(&lock);
    //銷燬條件變量
    pthread_cond_destroy(&need_prod);
    DestroyList(&pHead);
    return 0;
}

運行結果:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章