使用上pthread_cond_t遇到的死鎖問題
最近在一個項目中使用pthread_cond_t的時遇到一個死鎖的問題,特記錄分享一下。這個問題的使用場景很簡單,客戶端程序起兩個線程,一個線程發(fā)送數(shù)據(jù)給服務器,另一個線程接收服務器的返回。發(fā)送線程向服務器發(fā)送一個數(shù)據(jù)報,然后等待服務器返回(用pthread_cond_t等待),然后繼續(xù)發(fā)送下一個數(shù)據(jù)包……,如此循環(huán)下去。發(fā)送代碼抽取出來的邏輯像下面這樣:
static void* thread_fun_send(void* nouse)
{
intsend_index = 0;
while(1)
{
//模擬發(fā)送數(shù)據(jù)的socket系統(tǒng)調(diào)用
printf("send%d\n",send_index++);
//通知接收線程可以接收數(shù)據(jù)
pthread_mutex_lock(&mutex_send);
pthread_cond_signal(&cond_send);
pthread_mutex_unlock(&mutex_send);
//等待收到數(shù)據(jù)的pthread_cond_t
pthread_mutex_lock(&mutex_recv);
pthread_cond_wait(&cond_recv, &mutex_recv);
pthread_mutex_unlock(&mutex_recv);
}
}
接收線程等待服務器的返回,收到服務器返回后給發(fā)送線程發(fā)信號(用pthread_cond_t發(fā)信號),然后繼續(xù)等待接收服務器下一個返回……,如此循環(huán)下去。接收代碼抽取出來的邏輯像下面這樣:
static void* thread_fun_recv(void* nouse)
{
int recv_index = 0;
while(1)
{
//等待收到數(shù)據(jù)的pthread_cond_t,模擬阻塞的socket
pthread_mutex_lock(&mutex_send);
pthread_cond_wait(&cond_send, &mutex_send);
pthread_mutex_unlock(&mutex_send);
//模擬接收數(shù)據(jù)的socket系統(tǒng)調(diào)用
printf("recv %d\n",recv_index++);
//通知發(fā)送線程數(shù)據(jù)已經(jīng)收到
pthread_mutex_lock(&mutex_recv);
pthread_cond_signal(&cond_recv);
pthread_mutex_unlock(&mutex_recv);
}
}
就這么一個簡單的應用場景,看起來好像是很合乎邏輯的,但在實際的運行中會出現(xiàn)死鎖的問題。經(jīng)過綜合分析后發(fā)現(xiàn)其實是在理解pthread_cond_t上不準確了,一開始會認為它和windows中的Event是一樣的,只是調(diào)用的API函數(shù)名不同而已,其實它們是不同的。且不說自動重置這個功能,在信號的處理上它們也是完全不同的。在Windows中,當Event被置為有信號時,內(nèi)核會檢查有沒有在等待這個信號的線程,如果有則喚醒它,如果沒有信號會保存起來,下一個等待此信號的線程進來時會直接獲得信號而繼續(xù)運行;但在Linux中, 如果pthread_cond_t被置為有信號時,內(nèi)核也是會檢查有沒有在等待這個信號的線程,如果有則喚醒它,但如果沒有等待的線程內(nèi)核則不會保存這個信號(好像這個信號被丟掉了一樣),下一個等待信號的線程進來時是不會獲得信號而只會進入睡眠的,只有在睡眠后下一次pthread_cond_t被置為有信號時,這個線程才會被喚醒。
搞清楚了pthread_cond_t和Event的邏輯之后,對于上面這個客戶端的業(yè)務邏輯,用最小的改動方式就是把使用pthread_cond_t改為sem_t問題就解決了。
附:測試代碼,用下面命令即可編譯
gcc -o test_cond test_cond.c -pthread
點擊(此處)折疊或打開
聯(lián)系客服