1. 程式人生 > >Linux 多執行緒同步問題 條件變數

Linux 多執行緒同步問題 條件變數

互斥鎖:用來上鎖。

條件變數:用來等待,當條件變數用來自動阻塞一個執行緒,直到某特殊情況發生為止。通常條件變數和互斥鎖同時使用。

函式介紹:

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;

3.

名稱:

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。

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

#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程序*/
        else        
             printf("thead1:%d/n",i);
        pthread_mutex_unlock(&mutex);/*解鎖互斥量*/

sleep(1);
}

}

void *thread2(void *junk)
{
    
while(i<9)
    {
        pthread_mutex_lock(&mutex);

if(i%3!=0)
        
    pthread_cond_wait(&cond,&mutex);/*等待*/
        
printf("thread2:%d/n",i);
        pthread_mutex_unlock(&mutex);

sleep(1);
}

}

程式建立了2個新執行緒使他們同步執行,實現程序t_b列印20以內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

thread1:1

thread1:2

thread2:3

thread1:4

thread1:5

thread2:6

thread1:7

thread1:8

thread2:9

備註:

 pthread_cond_wait 執行的流程首先將這個mutex解鎖, 然後等待條件變數被喚醒, 如果沒有被喚醒, 該執行緒將一直休眠, 也就是說, 該執行緒將一直阻塞在這個pthread_cond_wait呼叫中, 而當此執行緒被喚醒時, 將自動將這個mutex加鎖,然後再進行條件變數判斷(原因是“驚群效應”,如果是多個執行緒都在等待這個條件,而同時只能有一個執行緒進行處理,此時就必須要再次條件判斷,以使只有一個執行緒進入臨界區處理。),如果滿足,則執行緒繼續執行,最後解鎖,

也就是說pthread_cond_wait實際上可以看作是以下幾個動作的合體:
解鎖執行緒鎖
等待執行緒喚醒,並且條件為true
加鎖執行緒鎖.

 pthread_cond_signal僅僅負責喚醒正在阻塞在同一條件變數上的一個執行緒,如果存在多個執行緒,系統自動根據排程策略決定喚醒其中的一個執行緒,在多處理器上,該函式是可能同時喚醒多個執行緒,同時該函式與鎖操作無關,解鎖是由pthread_mutex_unlock(&mutex)完成

喚醒丟失問題 
線上程並沒有阻塞在條件變數上時,呼叫pthread_cond_signal或pthread_cond_broadcast函式可能會引起喚醒丟失問題。

喚醒丟失往往會在下面的情況下發生:

一個執行緒呼叫pthread_cond_signal或pthread_cond_broadcast函式; 
另一個執行緒正處在測試條件變數和呼叫pthread_cond_wait函式之間; 
沒有執行緒正在處在阻塞等待的狀態下。