線程同步----條件變量,pthread_cond_wait()與pthread_cond_signal()到底幹了什麼

一、什麼是條件變量

       與互斥鎖不同,條件變量是用來等待而不是用來上鎖的。條件變量用來自動阻塞一個線程,直到某特殊情況發生爲止。通常條件變量和互斥鎖同時使用。

       條件變量使我們可以睡眠等待某種條件出現。條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:一個線程等待"條件變量的條件成立"而掛起;另一個線程使"條件成立"(給出條件成立信號)。

條 件的檢測是在互斥鎖的保護下進行的。如果一個條件爲假,一個線程自動阻塞,並釋放等待狀態改變的互斥鎖。如果另一個線程改變了條件,它發信號給關聯的條件 變量,喚醒一個或多個等待它的線程,重新獲得互斥鎖,重新評價條件。如果兩進程共享可讀寫的內存,條件變量可以被用來實現這兩進程間的線程同步。

       使用條件變量之前要先進行初始化。可以在單個語句中生成和初始化一個條件變量如:

pthread_cond_t my_condition=PTHREAD_COND_INITIALIZER;(用於進程間線程的通信)。

也可以利用函數pthread_cond_init動態初始化。

 

二、條件變量函數

1.

名稱:

pthread_cond_init

目標:

條件變量初始化

頭文件:

#include < pthread.h>

函數原形:

int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

參數:

cptr  條件變量

attr  條件變量屬性

返回值:

成功返回0,出錯返回錯誤編號。

     

pthread_cond_init函數可以用來初始化一個條件變量。他使用變量attr所指定的屬性來初始化一個條件變量,如果參數attr爲空,那麼它將使用缺省的屬性來設置所指定的條件變量。

2.

名稱:

pthread_cond_destroy

目標:

條件變量摧毀

頭文件:

#include < pthread.h>

函數原形:

int pthread_cond_destroy(pthread_cond_t *cond);

參數:

cptr  條件變量

返回值:

成功返回0,出錯返回錯誤編號。

      

pthread_cond_destroy函數可以用來摧毀所指定的條件變量,同時將會釋放所給它分配的資源。調用該函數的進程也並不要求等待在參數所指定的條件變量上。

 

 

3.

名稱:

pthread_cond_wait/pthread_cond_timedwait

目標:

條件變量等待

頭文件:

#include < pthread.h>

函數原形:

int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);

int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t mytex,const struct timespec *abstime);

參數:

cond 條件變量

mutex 互斥鎖

返回值:

成功返回0,出錯返回錯誤編號。

      

第一個參數*cond是指向一個條件變量的指針。第二個參數*mutex則是對相關的互斥鎖的指針。函數pthread_cond_timedwait函數類型與函數pthread_cond_wait,區別在於,如果達到或是超過所引用的參數*abstime,它將結束並返回錯誤ETIME.pthread_cond_timedwait函數的參數*abstime指向一個timespec結構。該結構如下:

typedef struct timespec{

       time_t tv_sec;

       long tv_nsex;

}timespec_t;

 

 

4.

名稱:

pthread_cond_signal/pthread_cond_broadcast

目標:

條件變量通知

頭文件:

#include < pthread.h>

函數原形:

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

參數:

cond 條件變量

返回值:

成功返回0,出錯返回錯誤編號。

      

參數*cond是對類型爲pthread_cond_t 的一個條件變量的指針。當調用pthread_cond_signal時一個在相同條件變量上阻塞的線程將被解鎖。如果同時有多個線程阻塞,則由調度策略確定接收通知的線程。如果調用pthread_cond_broadcast,則將通知阻塞在這個條件變量上的所有線程。一旦被喚醒,線程仍然會要求互斥鎖。如果當前沒有線程等待通知,則上面兩種調用實際上成爲一個空操作。如果參數*cond指向非法地址,則返回值EINVAL。

 

下面是一個簡單的例子,我們可以從程序的運行來了解條件變量的作用。

/*
 *程序創建了2個新線程使他們同步運行,實現進程t_b打印10以內3的倍數,t_a打印其他的數,程序開始線程t_b不滿足條件等待,
 * 線程t_a運行使a循環加1並打印。直到i爲3的倍數時,線程t_a發送信號通知進程t_b,這時t_b滿足條件,打印i值。
 *********************************************************************************************************
 */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*靜態初始化互斥鎖*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化條件變量*/
void *thread1(void *);
void *thread2(void *);
int i=1;


int main(void)
{
    pthread_t t_a;
    pthread_t t_b;
    pthread_create(&t_a,NULL,thread2,(void *)NULL);/*創建進程t_a*/
    pthread_create(&t_b,NULL,thread1,(void *)NULL); /*創建進程t_b*/
    pthread_join(t_b, NULL);/*等待進程t_b結束*/
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    exit(0);
}


void *thread1(void *junk)
{
    for(i=1;i<=9;i++)
    {
        pthread_mutex_lock(&mutex);/*鎖住互斥量*/
        if(i%3==0)
        {
             pthread_cond_signal(&cond);/*條件改變,發送信號,通知t_b進程*/
             sleep(1);
             printf("已喚醒thread2\n");
        }
        else
        {
             printf("thread1:%d\n",i);
        }
        pthread_mutex_unlock(&mutex);/*解鎖互斥量*/
        printf("thread1 unlock\n");
        sleep(1);
    }


}


void *thread2(void *junk)
{
    while(i<9)
    {
        pthread_mutex_lock(&mutex);
        sleep(1);
        printf("thread2 get lock\n");
        if(i%3!=0)
        {
        printf("開始開始等待\n");
        pthread_cond_wait(&cond,&mutex);/*等待*/
        printf("結束等待\n");
        printf("thread2:%d\n",i);
        }
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}

程序創建了2個新線程使他們同步運行,實現進程t_b打印10以內3的倍數,t_a打印其他的數,程序開始線程t_b不滿足條件等待,線程t_a運行使a循環加1並打印。直到i爲3的倍數時,線程t_a發送信號通知進程t_b,這時t_b滿足條件,打印i值。

下面是運行結果:

#cc –lpthread –o cond cond.c

#./cond

1,thread2 get lock         thread2獲得鎖

2,開始開始等待               pthread_cond_wait解鎖,再阻塞

3,thread1:1                    thread1獲得鎖

4,thread1 unlock           thread1釋放鎖

5,thread1:2                    thread2阻塞中不會競爭鎖,thread1再次獲得鎖

6,thread1 unlock           thread1釋放鎖

7,已喚醒thread2            此時i=3,喚醒thread2,thread2進入就緒狀態

8,thread1 unlock           thread1釋放鎖

9,結束等待                      thread2獲得鎖退出阻塞,執行pthread_cond_wait後的代碼

10,thread2:3                    打印這句後thread2解鎖

11,thread2 get lock         thread2獲得鎖, <爲什麼此時不是thread1獲得鎖,爲了讓thread1獲得鎖,我在thread2解鎖後還sleep(1)了,但還是 thread2獲得鎖>

12,開始開始等待               pthread_cond_wait解鎖,再阻塞

13,thread1:4                    。。。

14,thread1 unlock           。。。

15,thread1:5

16,thread1 unlock

17,已喚醒thread2

18,thread1 unlock

19,結束等待

20,thread2:6

21,thread1:7

22,thread1 unlock

23,thread2 get lock

24,開始開始等待

25,thread1:8

26,thread1 unlock

27,已喚醒thread2

28,thread1 unlock

29,結束等待

30,thread2:9

 

結論:

pthread_cond_wait() 必須與pthread_mutex_lock() 配套使用。pthread_cond_wait()函數一進入wait狀態就會自動解鎖(有原子操作,先解鎖,再阻塞)。當其他線程(線程乙)通過pthread_cond_signal()或pthread_cond_broadcast,把該線程(線程甲)喚醒,使pthread_cond_wait()進入就緒狀態

1,如果此時發信號pthread_cond_signal()的線程乙已經釋放鎖,線程甲自動獲得該mutex,繼續執行pthread_cond_wait()後的代碼。

2,如果此時發信號pthread_cond_signal()的線程乙還沒有釋放鎖,則線程甲繼續保持就緒狀態直到線程乙釋放鎖後,線程甲再獲得mutex

 

程序詳解參考如下鏈接

https://weihe6666.iteye.com/blog/1170141

但這個鏈接說

pthread_cond_signal(&cond)有原子操作(先解鎖,再發送信號)????

 

問題:

①,上述結論是我根據執行結果做的猜想,真實情況是否是我猜想的這樣?

②,我用上述的方法是否可以證明pthread_cond_signal(&cond)沒有原子操作,並不會解鎖,只是發信號????

③,執行結果的11步爲什麼不是thread1獲得鎖?

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