小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

多線程互斥

 mrjbydd 2012-08-24

條件變量函數(shù)

 

操作

相關(guān)函數(shù)說明

初始化條件變量

pthread_cond_init 語法

基于條件變量阻塞

pthread_cond_wait 語法

解除阻塞特定線程

pthread_cond_signal 語法

在指定的時(shí)間之前阻塞

pthread_cond_timedwait 語法

在指定的時(shí)間間隔內(nèi)阻塞

pthread_cond_reltimedwait_np 語法

解除阻塞所有線程

pthread_cond_broadcast 語法

銷毀條件變量狀態(tài)

pthread_cond_destroy 語法

初始化條件變量

使用 pthread_cond_init(3C) 可以將 cv 所指示的條件變量初始化為其缺省值,或者指定已經(jīng)使用 pthread_condattr_init() 設(shè)置的條件變量屬性。

pthread_cond_init 語法

int pthread_cond_init(pthread_cond_t *cv,

const pthread_condattr_t *cattr);
#include <pthread.h>



pthread_cond_t cv;

pthread_condattr_t cattr;

int ret;



/* initialize a condition variable to its default value */

ret = pthread_cond_init(&cv, NULL);



/* initialize a condition variable */

ret = pthread_cond_init(&cv, &cattr);

cattr 設(shè)置為 NULL。將 cattr 設(shè)置為 NULL 與傳遞缺省條件變量屬性對象的地址等效,但是沒有內(nèi)存開銷。對于 Solaris 線程,請參見cond_init 語法。

使用 PTHREAD_COND_INITIALIZER 宏可以將以靜態(tài)方式定義的條件變量初始化為其缺省屬性。PTHREAD_COND_INITIALIZER 宏與動(dòng)態(tài)分配具有 null 屬性的 pthread_cond_init() 等效,但是不進(jìn)行錯(cuò)誤檢查。

多個(gè)線程決不能同時(shí)初始化或重新初始化同一個(gè)條件變量。如果要重新初始化或銷毀某個(gè)條件變量,則應(yīng)用程序必須確保該條件變量未被使用。

pthread_cond_init 返回值

pthread_cond_init() 在成功完成之后會(huì)返回零。其他任何返回值都表示出現(xiàn)了錯(cuò)誤。如果出現(xiàn)以下任一情況,該函數(shù)將失敗并返回對應(yīng)的值。

EINVAL

描述:

cattr 指定的值無效。

EBUSY

描述:

條件變量處于使用狀態(tài)。

EAGAIN

描述:

必要的資源不可用。

ENOMEM

描述:

內(nèi)存不足,無法初始化條件變量。

基于條件變量阻塞

使用 pthread_cond_wait(3C) 可以以原子方式釋放 mp 所指向的互斥鎖,并導(dǎo)致調(diào)用線程基于 cv 所指向的條件變量阻塞。對于 Solaris 線程,請參見cond_wait 語法。

pthread_cond_wait 語法

int pthread_cond_wait(pthread_cond_t *cv,pthread_mutex_t *mutex);
#include <pthread.h>



pthread_cond_t cv;

pthread_mutex_t mp;

int ret;



/* wait on condition variable */

ret = pthread_cond_wait(&cv, &mp);

阻塞的線程可以通過 pthread_cond_signal()pthread_cond_broadcast() 喚醒,也可以在信號傳送將其中斷時(shí)喚醒。

不能通過 pthread_cond_wait() 的返回值來推斷與條件變量相關(guān)聯(lián)的條件的值的任何變化。必須重新評估此類條件。

pthread_cond_wait() 例程每次返回結(jié)果時(shí)調(diào)用線程都會(huì)鎖定并且擁有互斥鎖,即使返回錯(cuò)誤時(shí)也是如此。

該條件獲得信號之前,該函數(shù)一直被阻塞。該函數(shù)會(huì)在被阻塞之前以原子方式釋放相關(guān)的互斥鎖,并在返回之前以原子方式再次獲取該互斥鎖。

通常,對條件表達(dá)式的評估是在互斥鎖的保護(hù)下進(jìn)行的。如果條件表達(dá)式為假,線程會(huì)基于條件變量阻塞。然后,當(dāng)該線程更改條件值時(shí),另一個(gè)線程會(huì)針對條件變量發(fā)出信號。這種變化會(huì)導(dǎo)致所有等待該條件的線程解除阻塞并嘗試再次獲取互斥鎖。

必須重新測試導(dǎo)致等待的條件,然后才能從 pthread_cond_wait() 處繼續(xù)執(zhí)行。喚醒的線程重新獲取互斥鎖并從 pthread_cond_wait() 返回之前,條件可能會(huì)發(fā)生變化。等待線程可能并未真正喚醒。建議使用的測試方法是,將條件檢查編寫為調(diào)用 pthread_cond_wait()while() 循環(huán)。

pthread_mutex_lock();

while(condition_is_false)

pthread_cond_wait();

pthread_mutex_unlock();

如果有多個(gè)線程基于該條件變量阻塞,則無法保證按特定的順序獲取互斥鎖。


注 –

pthread_cond_wait() 是取消點(diǎn)。如果取消處于暫掛狀態(tài),并且調(diào)用線程啟用了取消功能,則該線程會(huì)終止,并在繼續(xù)持有該鎖的情況下開始執(zhí)行清除處理程序。


pthread_cond_wait 返回值

pthread_cond_wait() 在成功完成之后會(huì)返回零。其他任何返回值都表示出現(xiàn)了錯(cuò)誤。如果出現(xiàn)以下情況,該函數(shù)將失敗并返回對應(yīng)的值。

EINVAL

描述:

cvmp 指定的值無效。

解除阻塞一個(gè)線程

對于基于 cv 所指向的條件變量阻塞的線程,使用 pthread_cond_signal(3C) 可以解除阻塞該線程。對于 Solaris 線程,請參見cond_signal 語法。

pthread_cond_signal 語法

int pthread_cond_signal(pthread_cond_t *cv);
#include <pthread.h>



pthread_cond_t cv;

int ret;



/* one condition variable is signaled */

ret = pthread_cond_signal(&cv);

應(yīng)在互斥鎖的保護(hù)下修改相關(guān)條件,該互斥鎖用于獲得信號的條件變量中。否則,可能在條件變量的測試和 pthread_cond_wait() 阻塞之間修改該變量,這會(huì)導(dǎo)致無限期等待。

調(diào)度策略可確定喚醒阻塞線程的順序。對于 SCHED_OTHER,將按優(yōu)先級順序喚醒線程。

如果沒有任何線程基于條件變量阻塞,則調(diào)用 pthread_cond_signal() 不起作用。


示例 4–8 使用 pthread_cond_wait()pthread_cond_signal()

 

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);

}

pthread_cond_signal 返回值

pthread_cond_signal() 在成功完成之后會(huì)返回零。其他任何返回值都表示出現(xiàn)了錯(cuò)誤。如果出現(xiàn)以下情況,該函數(shù)將失敗并返回對應(yīng)的值。

EINVAL

描述:

cv 指向的地址非法。

說明了如何使用 pthread_cond_wait()pthread_cond_signal()。

在指定的時(shí)間之前阻塞

pthread_cond_timedwait(3C) 的用法與 pthread_cond_wait() 的用法基本相同,區(qū)別在于在由 abstime 指定的時(shí)間之后 pthread_cond_timedwait() 不再被阻塞。

pthread_cond_timedwait 語法

int pthread_cond_timedwait(pthread_cond_t *cv,

pthread_mutex_t *mp, const struct timespec *abstime);
#include <pthread.h>

#include <time.h>



pthread_cond_t cv;

pthread_mutex_t mp;

timestruct_t abstime;

int ret;



/* wait on condition variable */

ret = pthread_cond_timedwait(&cv, &mp, &abstime);

pthread_cond_timewait() 每次返回時(shí)調(diào)用線程都會(huì)鎖定并且擁有互斥鎖,即使 pthread_cond_timedwait() 返回錯(cuò)誤時(shí)也是如此。 對于 Solaris 線程

pthread_cond_timedwait() 函數(shù)會(huì)一直阻塞,直到該條件獲得信號,或者最后一個(gè)參數(shù)所指定的時(shí)間已過為止。


注 –

pthread_cond_timedwait() 也是取消點(diǎn)。



示例 4–9 計(jì)時(shí)條件等待

 

pthread_timestruc_t to;

pthread_mutex_t m;

pthread_cond_t c;

...

pthread_mutex_lock(&m);

to.tv_sec = time(NULL) + TIMEOUT;

to.tv_nsec = 0;

while (cond == FALSE) {

err = pthread_cond_timedwait(&c, &m, &to);

if (err == ETIMEDOUT) {

/* timeout, do something */

break;

}

}

pthread_mutex_unlock(&m);

pthread_cond_timedwait 返回值

pthread_cond_timedwait() 在成功完成之后會(huì)返回零。其他任何返回值都表示出現(xiàn)了錯(cuò)誤。如果出現(xiàn)以下任一情況,該函數(shù)將失敗并返回對應(yīng)的值。

EINVAL

描述:

cvabstime 指向的地址非法。

ETIMEDOUT

描述:

abstime 指定的時(shí)間已過。

超時(shí)會(huì)指定為當(dāng)天時(shí)間,以便在不重新計(jì)算值的情況下高效地重新測試條件,如示例 4–9 中所示。

在指定的時(shí)間間隔內(nèi)阻塞

pthread_cond_reltimedwait_np(3C) 的用法與 pthread_cond_timedwait() 的用法基本相同,唯一的區(qū)別在于 pthread_cond_reltimedwait_np() 會(huì)采用相對時(shí)間間隔而不是將來的絕對時(shí)間作為其最后一個(gè)參數(shù)的值。

pthread_cond_reltimedwait_np 語法

int  pthread_cond_reltimedwait_np(pthread_cond_t *cv, 

pthread_mutex_t *mp,

const struct timespec *reltime);
#include <pthread.h>

#include <time.h>



pthread_cond_t cv;

pthread_mutex_t mp;

timestruct_t reltime;

int ret;



/* wait on condition variable */

ret = pthread_cond_reltimedwait_np(&cv, &mp, &reltime);

pthread_cond_reltimedwait_np() 每次返回時(shí)調(diào)用線程都會(huì)鎖定并且擁有互斥鎖,即使 pthread_cond_reltimedwait_np() 返回錯(cuò)誤時(shí)也是如此。對于 Solaris 線程,請參見 cond_reltimedwait(3C)。pthread_cond_reltimedwait_np() 函數(shù)會(huì)一直阻塞,直到該條件獲得信號,或者最后一個(gè)參數(shù)指定的時(shí)間間隔已過為止。


注 –

pthread_cond_reltimedwait_np() 也是取消點(diǎn)。


pthread_cond_reltimedwait_np 返回值

pthread_cond_reltimedwait_np() 在成功完成之后會(huì)返回零。其他任何返回值都表示出現(xiàn)了錯(cuò)誤。如果出現(xiàn)以下任一情況,該函數(shù)將失敗并返回對應(yīng)的值。

EINVAL

描述:

cvreltime 指示的地址非法。

ETIMEDOUT

描述:

reltime 指定的時(shí)間間隔已過。

解除阻塞所有線程

對于基于 cv 所指向的條件變量阻塞的線程,使用 pthread_cond_broadcast(3C) 可以解除阻塞所有這些線程,這由 pthread_cond_wait() 來指定。

pthread_cond_broadcast 語法

int pthread_cond_broadcast(pthread_cond_t *cv);
#include <pthread.h>



pthread_cond_t cv;

int ret;



/* all condition variables are signaled */

ret = pthread_cond_broadcast(&cv);

如果沒有任何線程基于該條件變量阻塞,則調(diào)用 pthread_cond_broadcast() 不起作用。對于 Solaris 線程,請參見cond_broadcast 語法。

由于 pthread_cond_broadcast() 會(huì)導(dǎo)致所有基于該條件阻塞的線程再次爭用互斥鎖,因此請謹(jǐn)慎使用 pthread_cond_broadcast()。例如,通過使用 pthread_cond_broadcast(),線程可在資源釋放后爭用不同的資源量,如示例 4–10 中所示。


示例 4–10 條件變量廣播

 

pthread_mutex_t rsrc_lock;

pthread_cond_t rsrc_add;

unsigned int resources;



get_resources(int amount)

{

pthread_mutex_lock(&rsrc_lock);

while (resources < amount) {

pthread_cond_wait(&rsrc_add, &rsrc_lock);

}

resources -= amount;

pthread_mutex_unlock(&rsrc_lock);

}



add_resources(int amount)

{

pthread_mutex_lock(&rsrc_lock);

resources += amount;

pthread_cond_broadcast(&rsrc_add);

pthread_mutex_unlock(&rsrc_lock);

}

請注意,在 add_resources() 中,首先更新 resources 還是首先在互斥鎖中調(diào)用 pthread_cond_broadcast() 無關(guān)緊要。

應(yīng)在互斥鎖的保護(hù)下修改相關(guān)條件,該互斥鎖用于獲得信號的條件變量中。否則,可能在條件變量的測試和 pthread_cond_wait() 阻塞之間修改該變量,這會(huì)導(dǎo)致無限期等待。

pthread_cond_broadcast 返回值

pthread_cond_broadcast() 在成功完成之后會(huì)返回零。其他任何返回值都表示出現(xiàn)了錯(cuò)誤。如果出現(xiàn)以下情況,該函數(shù)將失敗并返回對應(yīng)的值。

EINVAL

描述:

cv 指示的地址非法。

銷毀條件變量狀態(tài)

使用 pthread_cond_destroy(3C) 可以銷毀與 cv 所指向的條件變量相關(guān)聯(lián)的任何狀態(tài)。對于 Solaris 線程,請參見cond_destroy 語法。

pthread_cond_destroy 語法

int pthread_cond_destroy(pthread_cond_t *cv);
#include <pthread.h>



pthread_cond_t cv;

int ret;



/* Condition variable is destroyed */

ret = pthread_cond_destroy(&cv);

請注意,沒有釋放用來存儲(chǔ)條件變量的空間。

pthread_cond_destroy 返回值

pthread_cond_destroy() 在成功完成之后會(huì)返回零。其他任何返回值都表示出現(xiàn)了錯(cuò)誤。如果出現(xiàn)以下情況,該函數(shù)將失敗并返回對應(yīng)的值。

EINVAL

描述:

cv 指定的值無效。

注意:pthread_cond_destroy 銷毀一個(gè)條件變量,釋放它擁有的資源。進(jìn)入 pthread_cond_destroy 之前,必須沒有在該條件變量上等待的線程。
Attempting to destroy a condition variable upon which other threads are currently blocked results in undefined behavior.

喚醒丟失問題

如果線程未持有與條件相關(guān)聯(lián)的互斥鎖,則調(diào)用 pthread_cond_signal()pthread_cond_broadcast() 會(huì)產(chǎn)生喚醒丟失錯(cuò)誤。

滿足以下所有條件時(shí),即會(huì)出現(xiàn)喚醒丟失問題:

  • 一個(gè)線程調(diào)用 pthread_cond_signal()pthread_cond_broadcast()

  • 另一個(gè)線程已經(jīng)測試了該條件,但是尚未調(diào)用 pthread_cond_wait()

  • 沒有正在等待的線程

    信號不起作用,因此將會(huì)丟失

僅當(dāng)修改所測試的條件但未持有與之相關(guān)聯(lián)的互斥鎖時(shí),才會(huì)出現(xiàn)此問題。只要僅在持有關(guān)聯(lián)的互斥鎖同時(shí)修改所測試的條件,即可調(diào)用 pthread_cond_signal()pthread_cond_broadcast(),而無論這些函數(shù)是否持有關(guān)聯(lián)的互斥鎖。

生成方和使用者問題

并發(fā)編程中收集了許多標(biāo)準(zhǔn)的眾所周知的問題,生成方和使用者問題只是其中的一個(gè)問題。此問題涉及到一個(gè)大小限定的緩沖區(qū)和兩類線程(生成方和使用者),生成方將項(xiàng)放入緩沖區(qū)中,然后使用者從緩沖區(qū)中取走項(xiàng)。

生成方必須在緩沖區(qū)中有可用空間之后才能向其中放置內(nèi)容。使用者必須在生成方向緩沖區(qū)中寫入之后才能從中提取內(nèi)容。

條件變量表示一個(gè)等待某個(gè)條件獲得信號的線程隊(duì)列。

示例 4–11 中包含兩個(gè)此類隊(duì)列。一個(gè)隊(duì)列 (less) 針對生成方,用于等待緩沖區(qū)中出現(xiàn)空位置。另一個(gè)隊(duì)列 (more) 針對使用者,用于等待從緩沖槽位的空位置中提取其中包含的信息。該示例中還包含一個(gè)互斥鎖,因?yàn)槊枋鲈摼彌_區(qū)的數(shù)據(jù)結(jié)構(gòu)一次只能由一個(gè)線程訪問。


示例 4–11 生成方和使用者的條件變量問題

 

typedef struct {

char buf[BSIZE];

int occupied;

int nextin;

int nextout;

pthread_mutex_t mutex;

pthread_cond_t more;

pthread_cond_t less;

} buffer_t;



buffer_t buffer;

如示例 4–12 中所示,生成方線程獲取該互斥鎖以保護(hù) buffer 數(shù)據(jù)結(jié)構(gòu),然后,緩沖區(qū)確定是否有空間可用于存放所生成的項(xiàng)。如果沒有可用空間,生成方線程會(huì)調(diào)用 pthread_cond_wait()pthread_cond_wait() 會(huì)導(dǎo)致生成方線程連接正在等待 less 條件獲得信號的線程隊(duì)列。less 表示緩沖區(qū)中的可用空間。

與此同時(shí),在調(diào)用 pthread_cond_wait() 的過程中,該線程會(huì)釋放互斥鎖的鎖定。正在等待的生成方線程依賴于使用者線程在條件為真時(shí)發(fā)出信號,如示例 4–12 中所示。該條件獲得信號時(shí),將會(huì)喚醒等待 less 的第一個(gè)線程。但是,該線程必須再次鎖定互斥鎖,然后才能從 pthread_cond_wait() 返回。

獲取互斥鎖可確保該線程再次以獨(dú)占方式訪問緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu)。該線程隨后必須檢查緩沖區(qū)中是否確實(shí)存在可用空間。如果空間可用,該線程會(huì)向下一個(gè)可用的空位置中進(jìn)行寫入。

與此同時(shí),使用者線程可能正在等待項(xiàng)出現(xiàn)在緩沖區(qū)中。這些線程正在等待條件變量 more。剛在緩沖區(qū)中存儲(chǔ)內(nèi)容的生成方線程會(huì)調(diào)用 pthread_cond_signal() 以喚醒下一個(gè)正在等待的使用者。如果沒有正在等待的使用者,此調(diào)用將不起作用。

最后,生成方線程會(huì)解除鎖定互斥鎖,從而允許其他線程處理緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu)。


示例 4–12 生成方和使用者問題:生成方

 

void producer(buffer_t *b, char item)

{

pthread_mutex_lock(&b->mutex);



while (b->occupied >= BSIZE)

pthread_cond_wait(&b->less, &b->mutex);



assert(b->occupied < BSIZE);



b->buf[b->nextin++] = item;



b->nextin %= BSIZE;

b->occupied++;



/* now: either b->occupied < BSIZE and b->nextin is the index

of the next empty slot in the buffer, or

b->occupied == BSIZE and b->nextin is the index of the

next (occupied) slot that will be emptied by a consumer

(such as b->nextin == b->nextout) */



pthread_cond_signal(&b->more);



pthread_mutex_unlock(&b->mutex);

}

請注意 assert() 語句的用法。除非在編譯代碼時(shí)定義了 NDEBUG,否則 assert() 在其參數(shù)的計(jì)算結(jié)果為真(非零)時(shí)將不執(zhí)行任何操作。如果參數(shù)的計(jì)算結(jié)果為假(零),則該程序會(huì)中止。在多線程程序中,此類斷言特別有用。如果斷言失敗,assert() 會(huì)立即指出運(yùn)行時(shí)問題。assert() 還有另一個(gè)作用,即提供有用的注釋。

/* now: either b->occupied ... 開頭的注釋最好以斷言形式表示,但是由于語句過于復(fù)雜,無法用布爾值表達(dá)式來表示,因此將用英語表示。

斷言和注釋都是不變量的示例。這些不變量是邏輯語句,在程序正常執(zhí)行時(shí)不應(yīng)將其聲明為假,除非是線程正在修改不變量中提到的一些程序變量時(shí)的短暫修改過程中。當(dāng)然,只要有線程執(zhí)行語句,斷言就應(yīng)當(dāng)為真。

使用不變量是一種極為有用的方法。即使沒有在程序文本中聲明不變量,在分析程序時(shí)也應(yīng)將其視為不變量。

每次線程執(zhí)行包含注釋的代碼時(shí),生成方代碼中表示為注釋的不變量始終為真。如果將此注釋移到緊挨 mutex_unlock() 的后面,則注釋不一定仍然為真。如果將此注釋移到緊跟 assert() 之后的位置,則注釋仍然為真。

因此,不變量可用于表示一個(gè)始終為真的屬性,除非一個(gè)生成方或一個(gè)使用者正在更改緩沖區(qū)的狀態(tài)。線程在互斥鎖的保護(hù)下處理緩沖區(qū)時(shí),該線程可能會(huì)暫時(shí)聲明不變量為假。但是,一旦線程結(jié)束對緩沖區(qū)的操作,不變量即會(huì)恢復(fù)為真。

示例 4–13 給出了使用者的代碼。該邏輯流程與生成方的邏輯流程相對稱。


示例 4–13 生成方和使用者問題:使用者

 

char consumer(buffer_t *b)

{

char item;

pthread_mutex_lock(&b->mutex);

while(b->occupied <= 0)

pthread_cond_wait(&b->more, &b->mutex);



assert(b->occupied > 0);



item = b->buf[b->nextout++];

b->nextout %= BSIZE;

b->occupied--;



/* now: either b->occupied > 0 and b->nextout is the index

of the next occupied slot in the buffer, or

b->occupied == 0 and b->nextout is the index of the next

(empty) slot that will be filled by a producer (such as

b->nextout == b->nextin) */



pthread_cond_signal(&b->less);

pthread_mutex_unlock(&b->mutex);



return(item);

}

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多