?? 互斥鎖與條件變量.txt
字號(hào):
4.2 互斥鎖
互斥鎖用來(lái)保證一段時(shí)間內(nèi)只有一個(gè)線程在執(zhí)行一段代碼。必要性顯而易見(jiàn):假設(shè)各個(gè)線程向同一個(gè)文件順序?qū)懭霐?shù)據(jù),最后得到的結(jié)果一定是災(zāi)難性的。
我們先看下面一段代碼。這是一個(gè)讀/寫(xiě)程序,它們公用一個(gè)緩沖區(qū),并且我們假定一個(gè)緩沖區(qū)只能保存一條信息。即緩沖區(qū)只有兩個(gè)狀態(tài):有信息或沒(méi)有信息。
void reader_function ( void );
void writer_function ( void );
char buffer;
int buffer_has_item=0;
pthread_mutex_t mutex;
struct timespec delay;
void main ( void ){
pthread_t reader;
/* 定義延遲時(shí)間*/
delay.tv_sec = 2;
delay.tv_nec = 0;
/* 用默認(rèn)屬性初始化一個(gè)互斥鎖對(duì)象*/
pthread_mutex_init (&mutex,NULL);
pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);
writer_function( );
}
void writer_function (void){
while(1){
/* 鎖定互斥鎖*/
pthread_mutex_lock (&mutex);
if (buffer_has_item==0){
buffer=make_new_item( );
buffer_has_item=1;
}
/* 打開(kāi)互斥鎖*/
pthread_mutex_unlock(&mutex);
pthread_delay_np(&delay);
}
}
void reader_function(void){
while(1){
pthread_mutex_lock(&mutex);
if(buffer_has_item==1){
consume_item(buffer);
buffer_has_item=0;
}
pthread_mutex_unlock(&mutex);
pthread_delay_np(&delay);
}
}
這里聲明了互斥鎖變量mutex,結(jié)構(gòu)pthread_mutex_t為不公開(kāi)的數(shù)據(jù)類型,其中包含一個(gè)系統(tǒng)分配的屬性對(duì)象。函數(shù)pthread_mutex_init用來(lái)生成一個(gè)互斥鎖。NULL參數(shù)表明使用默認(rèn)屬性。如果需要聲明特定屬性的互斥鎖,須調(diào)用函數(shù)pthread_mutexattr_init。函數(shù)pthread_mutexattr_setpshared和函數(shù)pthread_mutexattr_settype用來(lái)設(shè)置互斥鎖屬性。前一個(gè)函數(shù)設(shè)置屬性pshared,它有兩個(gè)取值,PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用來(lái)不同進(jìn)程中的線程同步,后者用于同步本進(jìn)程的不同線程。在上面的例子中,我們使用的是默認(rèn)屬性PTHREAD_PROCESS_ PRIVATE。后者用來(lái)設(shè)置互斥鎖類型,可選的類型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它們分別定義了不同的上所、解鎖機(jī)制,一般情況下,選用最后一個(gè)默認(rèn)屬性。
pthread_mutex_lock聲明開(kāi)始用互斥鎖上鎖,此后的代碼直至調(diào)用pthread_mutex_unlock為止,均被上鎖,即同一時(shí)間只能被一個(gè)線程調(diào)用執(zhí)行。當(dāng)一個(gè)線程執(zhí)行到pthread_mutex_lock處時(shí),如果該鎖此時(shí)被另一個(gè)線程使用,那此線程被阻塞,即程序?qū)⒌却搅硪粋€(gè)線程釋放此互斥鎖。在上面的例子中,我們使用了pthread_delay_np函數(shù),讓線程睡眠一段時(shí)間,就是為了防止一個(gè)線程始終占據(jù)此函數(shù)。
上面的例子非常簡(jiǎn)單,就不再介紹了,需要提出的是在使用互斥鎖的過(guò)程中很有可能會(huì)出現(xiàn)死鎖:兩個(gè)線程試圖同時(shí)占用兩個(gè)資源,并按不同的次序鎖定相應(yīng)的互斥鎖,例如兩個(gè)線程都需要鎖定互斥鎖1和互斥鎖2,a線程先鎖定互斥鎖1,b線程先鎖定互斥鎖2,這時(shí)就出現(xiàn)了死鎖。此時(shí)我們可以使用函數(shù)pthread_mutex_trylock,它是函數(shù)pthread_mutex_lock的非阻塞版本,當(dāng)它發(fā)現(xiàn)死鎖不可避免時(shí),它會(huì)返回相應(yīng)的信息,程序員可以針對(duì)死鎖做出相應(yīng)的處理。另外不同的互斥鎖類型對(duì)死鎖的處理不一樣,但最主要的還是要程序員自己在程序設(shè)計(jì)注意這一點(diǎn)。
4.3 條件變量
前一節(jié)中我們講述了如何使用互斥鎖來(lái)實(shí)現(xiàn)線程間數(shù)據(jù)的共享和通信,互斥鎖一個(gè)明顯的缺點(diǎn)是它只有兩種狀態(tài):鎖定和非鎖定。而條件變量通過(guò)允許線程阻塞和等待另一個(gè)線程發(fā)送信號(hào)的方法彌補(bǔ)了互斥鎖的不足,它常和互斥鎖一起使用。使用時(shí),條件變量被用來(lái)阻塞一個(gè)線程,當(dāng)條件不滿足時(shí),線程往往解開(kāi)相應(yīng)的互斥鎖并等待條件發(fā)生變化。一旦其它的某個(gè)線程改變了條件變量,它將通知相應(yīng)的條件變量喚醒一個(gè)或多個(gè)正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖并重新測(cè)試條件是否滿足。一般說(shuō)來(lái),條件變量被用來(lái)進(jìn)行線承間的同步。
條件變量的結(jié)構(gòu)為pthread_cond_t,函數(shù)pthread_cond_init()被用來(lái)初始化一個(gè)條件變量。它的原型為:
extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));
其中cond是一個(gè)指向結(jié)構(gòu)pthread_cond_t的指針,cond_attr是一個(gè)指向結(jié)構(gòu)pthread_condattr_t的指針。結(jié)構(gòu)pthread_condattr_t是條件變量的屬性結(jié)構(gòu),和互斥鎖一樣我們可以用它來(lái)設(shè)置條件變量是進(jìn)程內(nèi)可用還是進(jìn)程間可用,默認(rèn)值是PTHREAD_ PROCESS_PRIVATE,即此條件變量被同一進(jìn)程內(nèi)的各個(gè)線程使用。注意初始化條件變量只有未被使用時(shí)才能重新初始化或被釋放。釋放一個(gè)條件變量的函數(shù)為pthread_cond_ destroy(pthread_cond_t cond)。
函數(shù)pthread_cond_wait()使線程阻塞在一個(gè)條件變量上。它的函數(shù)原型為:
extern int pthread_cond_wait __P ((pthread_cond_t *__cond,
pthread_mutex_t *__mutex));
線程解開(kāi)mutex指向的鎖并被條件變量cond阻塞。線程可以被函數(shù)pthread_cond_signal和函數(shù)pthread_cond_broadcast喚醒,但是要注意的是,條件變量只是起阻塞和喚醒線程的作用,具體的判斷條件還需用戶給出,例如一個(gè)變量是否為0等等,這一點(diǎn)我們從后面的例子中可以看到。線程被喚醒后,它將重新檢查判斷條件是否滿足,如果還不滿足,一般說(shuō)來(lái)線程應(yīng)該仍阻塞在這里,被等待被下一次喚醒。這個(gè)過(guò)程一般用while語(yǔ)句實(shí)現(xiàn)。
另一個(gè)用來(lái)阻塞線程的函數(shù)是pthread_cond_timedwait(),它的原型為:
extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,
pthread_mutex_t *__mutex, __const struct timespec *__abstime));
它比函數(shù)pthread_cond_wait()多了一個(gè)時(shí)間參數(shù),經(jīng)歷abstime段時(shí)間后,即使條件變量不滿足,阻塞也被解除。
函數(shù)pthread_cond_signal()的原型為:
extern int pthread_cond_signal __P ((pthread_cond_t *__cond));
它用來(lái)釋放被阻塞在條件變量cond上的一個(gè)線程。多個(gè)線程阻塞在此條件變量上時(shí),哪一個(gè)線程被喚醒是由線程的調(diào)度策略所決定的。要注意的是,必須用保護(hù)條件變量的互斥鎖來(lái)保護(hù)這個(gè)函數(shù),否則條件滿足信號(hào)又可能在測(cè)試條件和調(diào)用pthread_cond_wait函數(shù)之間被發(fā)出,從而造成無(wú)限制的等待。下面是使用函數(shù)pthread_cond_wait()和函數(shù)pthread_cond_signal()的一個(gè)簡(jiǎn)單的例子。
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;
decrement_count () {
pthread_mutex_lock (&count_lock);
while(count==0)
pthread_cond_wait( &count_nonzero, &count_lock);
count=count -1;
pthread_mutex_unlock (&count_lock);
}
increment_count(){
pthread_mutex_lock(&count_lock);
if(count==0)
pthread_cond_signal(&count_nonzero);
count=count+1;
pthread_mutex_unlock(&count_lock);
}
count值為0時(shí),decrement函數(shù)在pthread_cond_wait處被阻塞,并打開(kāi)互斥鎖count_lock。此時(shí),當(dāng)調(diào)用到函數(shù)increment_count時(shí),pthread_cond_signal()函數(shù)改變條件變量,告知decrement_count()停止阻塞。讀者可以試著讓兩個(gè)線程分別運(yùn)行這兩個(gè)函數(shù),看看會(huì)出現(xiàn)什么樣的結(jié)果。
函數(shù)pthread_cond_broadcast(pthread_cond_t *cond)用來(lái)喚醒所有被阻塞在條件變量cond上的線程。這些線程被喚醒后將再次競(jìng)爭(zhēng)相應(yīng)的互斥鎖,所以必須小心使用這個(gè)函數(shù)。
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -