線程的資料處理 之 互斥鎖
互斥鎖
互斥鎖用來保證一段時間內只有一個線程在執行一段代碼
pthread_mutex_lock聲明開始用互斥鎖上鎖,此後的代碼直至調用pthread_mutex_unlock為止,均被上鎖,即同一時間只能被一個線程調用執行。當一個線程執行到pthread_mutex_lock處時,如果該鎖此時被另一個線程使用,那此線程被阻塞,即程式將等待到另一個線程釋放此互斥鎖
a線程先鎖定互斥鎖1,b線程先鎖定互斥鎖2,這時就出現了死結。此時我們可以使用函數 pthread_mutex_trylock,它是函數pthread_mutex_lock的非阻塞版本,當它發現死結不可避免時,它會返回相應的資訊,程式員可以針對死結做出相應的處理。另外不同的互斥鎖類型對死結的處理不一樣,但最主要的還是要程式員自己在程式設計注意這一點。
條件變數
互斥鎖一個明顯的缺點是它只有兩種狀態:鎖定和非鎖定。而條件變數通過允許線程阻塞和等待另一個線程發送訊號的方法彌補了互斥鎖的不足,它常和互斥鎖一起使用。使用時,條件變數被用來阻塞一個線程,當條件不滿足時,線程往往解開相應的互斥鎖並等待條件發生變化。一旦其它的某個線程改變了條件變數,它將通知相應的條件變數喚醒一個或多個正被此條件變數阻塞的線程。這些線程將重新鎖定互斥鎖並重新測試條件是否滿足。一般說來,條件變數被用來進行線程間的同步.
條件變數的結構為pthread_cond_t,函數pthread_cond_init()被用來初始化一個條件變數。它的原型為:
extern int pthread_cond_init __P ((pthread_cond_t *__cond,
__const pthread_condattr_t *__cond_attr));
其中cond是一個指向結構pthread_cond_t的指標,cond_attr是一個指向結構pthread_condattr_t的指標。
結構 pthread_condattr_t是條件變數的屬性結構,和互斥鎖一樣我們可以用它來設定條件變數是進程內可用還是進程間可用,預設值是 PTHREAD_ PROCESS_PRIVATE,即此條件變數被同一進程內的各個線程使用。注意初始化條件變數只有未被使用時才能重新初始化或被釋放。
釋放一個條件變數的函數為pthread_cond_ destroy(pthread_cond_t cond)。
函數pthread_cond_wait()使線程阻塞在一個條件變數上。它的函數原型為:
extern int pthread_cond_wait __P ((pthread_cond_t *__cond,
pthread_mutex_t *__mutex));
線程解開mutex指向的鎖並被條件變數cond阻塞。線程可以被函數pthread_cond_signal和函數 pthread_cond_broadcast喚醒,但是要注意的是,條件變數只是起阻塞和喚醒線程的作用,具體的判斷條件還需使用者給出,例如一個變數是否為0等等,這一點我們從後面的例子中可以看到。線程被喚醒後,它將重新檢查判斷條件是否滿足,如果還不滿足,一般說來線程應該仍阻塞在這裡,被等待被下一次喚醒。這個過程一般用while語句實現。
另一個用來阻塞線程的函數是pthread_cond_timedwait(),它的原型為:
extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,
pthread_mutex_t *__mutex,
__const struct timespec *__abstime));
它比函數pthread_cond_wait()多了一個時間參數,經曆abstime段時間後,即使條件變數不滿足,阻塞也被解除。
函數pthread_cond_signal()的原型為:
extern int pthread_cond_signal __P ((pthread_cond_t *__cond));
它用來釋放被阻塞在條件變數cond上的一個線程。多個線程阻塞在此條件變數上時,哪一個線程被喚醒是由線程的調度策略所決定的。要注意的是,必須用保護條件變數的互斥鎖來保護這個函數,否則條件滿足訊號又可能在測試條件和調用pthread_cond_wait函數之間被發出,從而造成無限制的等待
下面是使用函數pthread_cond_wait()和函數pthread_cond_signal()的一個簡單的例子。
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
using namespace std;
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
int counter =0;
int estatus=-1;
void * decrement_count(void * argv);
void * increment_count(void * argv);
int main(int argc, char *argv[])
{
printf("counter:%d\n",counter);
pthread_t thrd1,thrd2;
int ret;
ret = pthread_create(&thrd1,NULL,decrement_count,NULL);
if(ret)
{
perror("de1:");
return 1;
}
ret = pthread_create(&thrd2,NULL,increment_count,NULL);
if(ret)
{
perror("in1:");
return 1;
}
int count=0;
while(count!=20)
{
printf("counter:%d\n",counter);
sleep(1);
count++;
}
return 0;
}
void * decrement_count (void * argv)
{
pthread_mutex_lock(&count_lock);
while(counter==0)
pthread_cond_wait(&count_nonzero,&count_lock);
counter--;
pthread_mutex_unlock(&count_lock);
return &estatus;
}
void * increment_count(void * argv)
{
pthread_mutex_lock(&count_lock);
if(counter==0)
pthread_cond_signal(&count_nonzero);
counter++;
pthread_mutex_unlock(&count_lock);
return &estatus;
}
開始時 counter 為0,ret = pthread_create(&thrd1,NULL,decrement_count,NULL)處產生一個thrd1線程運行decrement_count(),此線程內函數運行流程為:鎖定 互斥鎖 count_lock,如果counter為0,此線程被阻塞在條件變數count_nonzero上.同時釋放互斥鎖count_lock.
與此同時主程式還在運行,建立另一個線程thrd2運行 increment_count,此線程內的函數流程如下:鎖定 互斥鎖 count_lock,如果counter為0,喚醒在條件變數count_nonzero上的線程即thrd1.但是由於有互斥鎖count_lock,thrd1還是在等待.然後count++,釋放互斥鎖,.......thrd1由於互斥鎖釋放,重新判斷counter是不是為0,如果為0再把線程阻塞在條件變數count_nonzero上,但這時counter已經為1了.所以線程繼續運行.counter--釋放互斥鎖......與此主程式間隔列印counter運行一段時間退出.
後記,在編譯的時候加上 -lpthread