pthread_cond_wait和pthread_cond_signal的使用方法梳理

這兩個函數是多線程操作中非常重要的,也是相對來說難理解的。這裏梳理一下。

首先是函數介紹,

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)

這裏有兩個參數,cond和mutex。cond是條件,這個值可以用宏做聲明:

pthread_cond_t  cond = PTHREAD_COND_INITIALIZER;

也可以使用函數來聲成: 

int pthread_cond_init(pthread_cond_t *cond, pthread_cond_attr *cattr); 
int pthread_cond_destroy(pthread_cond_t *cond);

第一個函數是init,第二個爲destroy。函數中的cond便是條件變量。這裏需要說下,第一個函數中的cattr是屬性聲明,一般用NULL使用默認屬性,其屬性在函數返回時,寫道cond指向的內存。

另一個主角是:

int pthread_cond_signal(pthread_cond_t *cond)

這兩個一般搭配使用,pthread_cond_wait將當前線程阻塞等待,而在調用pthread_cond_signal後,將再次激活。當然,還有其它一些函數搭配使用,看下面的例子。

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//init mutex
pthread_cond_t  cond = PTHREAD_COND_INITIALIZER;//init cond

void *thread1(void*);
void *thread2(void*);

int i = 1; //global

int main(void){
    pthread_t p1;
    pthread_t p2;//two thread

    pthread_create(&p2,NULL,thread2,(void*)NULL);
    pthread_create(&p1,NULL,thread1,(void*)NULL);//Create thread
    
    pthread_join(p2,NULL);//wait a_b thread end
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
   exit(0);
}

void *thread1(void *parameter){
    for(i = 1;i<= 9; i++){
        pthread_mutex_lock(&mutex); //互斥鎖
        printf("\n\ncall thread1 \n");
        if(i%3 == 0)
        {
            pthread_cond_signal(&cond); //send sianal to p2
            printf("p1:%d step1\n", i);
       	}
        else
            printf("p1:%d step2\n",i);
        pthread_mutex_unlock(&mutex);

		printf("p1: sleep i=%d  step3\n", i);
        sleep(1);
		printf("p1: sleep i=%d  step4\n", i);
    }
}

void *thread2(void*parameter){
    while(i < 9)
    {
        pthread_mutex_lock(&mutex);
        printf("\n\ncall thread2 \n");
        if(i%3 != 0)
            pthread_cond_wait(&cond,&mutex); //wait signal from p1
        
		printf("p2: %d step1\n",i);
        pthread_mutex_unlock(&mutex);

		printf("p2: sleep i=%d step2\n", i);
        sleep(1);
		printf("p2: sleep i=%d step3\n", i);		
    }
}  

其運行結果爲

call thread1 
p1:1 step2
p1: sleep i=1  step3


call thread2 
p1: sleep i=1  step4


call thread1 
p1:2 step2
p1: sleep i=2  step3
p1: sleep i=2  step4


call thread1 
p1:3 step1
p1: sleep i=3  step3
p2: 3 step1
p2: sleep i=3 step2
p1: sleep i=3  step4


call thread1 
p1:4 step2
p1: sleep i=4  step3
p2: sleep i=4 step3


call thread2 
p1: sleep i=4  step4


call thread1 
p1:5 step2
p1: sleep i=5  step3
p1: sleep i=5  step4


call thread1 
p1:6 step1
p1: sleep i=6  step3
p2: 6 step1
p2: sleep i=6 step2
p1: sleep i=6  step4


call thread1 
p1:7 step2
p1: sleep i=7  step3
p2: sleep i=7 step3


call thread2 
p1: sleep i=7  step4


call thread1 
p1:8 step2
p1: sleep i=8  step3
p1: sleep i=8  step4


call thread1 
p1:9 step1
p1: sleep i=9  step3
p2: 9 step1
p2: sleep i=9 step2
p1: sleep i=9  step4
p2: sleep i=10 step3

接下來,一點點解釋。

第一階段,call thread1=>p1:1 step2=>p1: sleep i=1 step3

首先因爲p1優先級高,所以先call thread1,然後i%3!=0,所以運行p1:1 step2,接下來就是p1: sleep i=1 step3

需要注意的是,由於第三句話之前,已經pthread_mutex_unlock,解除了互斥鎖,所以p2是可以訪問共享資源了的。

在sleep 1秒的時間裏,發生了第二階段。

第二階段:call thread2=>p1: sleep i=1 sep4

這裏因爲 i % 3 != 0, 所以執行了pthread_cond_wait,進入了等待階段,p2後續的就先不運行。

這裏需要注意,雖然p2也上了鎖,但是pthread_cond_wait發揮了它的作用,將線程鎖解開了。

這就是他的一個作用pthread_cond_wait,先會解除當前線程的互斥鎖,然後掛線線程,等待條件變量滿足條件。一旦條件變量滿足條件,則會給線程上鎖,繼續執行pthread_cond_wait。

第三階段:call thread1=>p1:3 step1.......

回到pthread1,只時候i已經循環到3了,所以執行了pthread_cond_signal,條件變量又滿足了,所以p2的又被激活。

因此,打印出了p2: 3 step1=>p2: sleep i=3 step2。然後因爲p2有解除了鎖,在sleep時候,程序又切換到了p1線程。

後續的流程便是這樣了。

最後一個階段:

call thread1 
p1:9 step1
p1: sleep i=9  step3
p2: 9 step1
p2: sleep i=9 step2
p1: sleep i=9  step4
p2: sleep i=10 step3
 

因爲i已經到了9,不會再循環了,所以pthread1的最後一句便是p1: sleep i=9  step4。

但是,i還要再加1,所以傳到p2的時候,最後一句就成了p2: sleep i=10 step3。

 

梳理了一遍流程,感覺對線程間的理解更清晰了一些。

 

 

 

 

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