1. 程式人生 > >Linux 2. 關於pthread_cond_signal 喚醒兩個執行緒的問題

Linux 2. 關於pthread_cond_signal 喚醒兩個執行緒的問題

這裡貼出一個非常經典的關於多執行緒條件變數互斥鎖的案例,即生產消費者模型。

我想說的是這裡的while迴圈判斷pthread_cond_wait,為何不能使用if的問題。

根據man查詢到pthread_cond_signal這個函式至少會喚起1個執行緒,也就是說也有可能會喚起兩個及以上個執行緒。因此,如果這裡使用if而不是while,那麼當被喚起兩個執行緒時(考慮爭奪僅有的一個資源時的情況),會發生第二個執行緒沒有資源可以消費的情況,即發生head=NULL,後續操作出現斷錯誤!因此,這裡一定要使用while做一步迴圈判斷,這樣即使兩個執行緒都被喚起,那麼第二個執行緒會再次判斷head是否為NULL,如果是NULL,就再次wait。

程式碼如下,是他人寫的,但是我主要點出while(if)那裡:

/*藉助條件變數模擬,生產者-消費者問題*/                                                               
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

/*連結串列作為共享資料,需被互斥量保護*/
struct msg {
    struct msg *next;
    int num;
};

struct msg *head;
struct msg *mp;

/*靜態初始化一個全域性條件變數和一個全域性互斥量*/
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

void *consumer(void *p)
{
    for(;;){
        pthread_mutex_lock(&lock);
        if(head == NULL)
{ //頭指標為空,說明沒有節點。使用while而不使用if。 pthread_cond_wait(&has_product, &lock); //注意,在wait阻塞期間,該執行緒會釋放之前lock的鎖,因此生產者是可以加鎖的。每當有signal或者broadcast has_product時,該函式便返回,while再次判斷head是否為NULL,條件為否,因此可以向下執行。如果這裡是if而不是while的話,那麼直接往下執行。而signal的傳送是保證至少有一個執行緒是捕獲到,因此如果有兩個執行緒同時捕捉到,並且wait返回時,雖然一個執行緒是會鎖定互斥變數,成功操作,但是另一個執行緒由於沒有再次判斷head是否為NULL,因此等他獲得鎖再操作時,其實head已經為NULL了(上一個執行緒消費了唯一的一個產品),因此發生段錯誤。因此這裡必須要使用while,從而可以再次wait檢查鎖佔用情況。 } mp = head; head = mp->next; //模擬消費掉一個產品 printf("-Consume ---%d\n", mp->num); free(mp); mp = NULL; pthread_mutex_unlock(&lock); //sleep(rand() % 5); } } void *producer(void *p) { for(;;){ pthread_mutex_lock(&lock);//必須在mp之前,防止mp剛malloc出來就又被新malloc出來的替代。 mp = malloc(sizeof(struct msg)); //模擬生產一個產品 mp->num = rand() % 1000 + 1; printf("-Produce ---%d\n", mp->num); mp->next = head; head = mp; pthread_mutex_unlock(&lock); //將等待在該條件變數上的一個執行緒喚醒 pthread_cond_signal(&has_product); //sleep(rand() % 5); } } int main(int argc, char * argv) { pthread_t pid, cid; srand(time(NULL)); pthread_create(&pid, NULL, producer, NULL); pthread_create(&cid, NULL, consumer, NULL); pthread_join(pid, NULL); pthread_join(cid, NULL); return 0; }

錯誤如下: