Linux多线程实践(六)使用Posix条件变量解决生产者消费者问题
前面的一片文章我們已經(jīng)講過使用信號量解決生產(chǎn)者消費(fèi)者問題。那么什么情況下我們須要引入條件變量呢?
這里借用 ?http://www.cnblogs.com/ngnetboy/p/3521547.html 的解釋:
如果有共享的資源sum,與之相關(guān)聯(lián)的mutex?是lock_s.如果每一個線程對sum的操作非常easy的,與sum的狀態(tài)無關(guān),比方僅僅是sum++.那么僅僅用mutex足夠了.程序猿僅僅要確保每一個線程操作前,取得lock,然后sum++,再unlock就可以.每一個線程的代碼將像這樣:
add() {pthread_mutex_lock(lock_s);sum++;pthread_mutex_unlock(lock_s); }假設(shè)操作比較復(fù)雜,假設(shè)線程t0,t1,t2的操作是sum++,而線程t3則是在sum到達(dá)100的時候,打印出一條信息,并對sum清零.?這樣的情況下,假設(shè)僅僅用mutex,?則t3須要一個循環(huán),每一個循環(huán)里先取得lock_s,然后檢查sum的狀態(tài),假設(shè)sum>=100,則打印并清零,然后unlock.假設(shè)sum<100,則unlock,并sleep()本線程合適的一段時間。
這個時候,t0,t1,t2的代碼不變,t3的代碼例如以下:
print() {while (1){pthread_mutex_lock(lock_s);if(sum<100){printf(“sum reach 100!”);pthread_mutex_unlock(lock_s);}else{pthread_mutex_unlock(lock_s);my_thread_sleep(100);return OK;}} }
這樣的辦法有兩個問題
1)?sum在大多數(shù)情況下不會到達(dá)100,那么對t3的代碼來說,大多數(shù)情況下,走的是else分支,僅僅是lock和unlock,然后sleep().這浪費(fèi)了CPU處理時間.
2)?為了節(jié)省CPU處理時間,t3會在探測到sum沒到達(dá)100的時候sleep()一段時間.這樣卻又帶來另外一個問題,亦即t3響應(yīng)速度下降.可能在sum到達(dá)200的時候,t4才會醒過來.
3)?這樣,程序猿在設(shè)置sleep()時間的時候陷入兩難境界,設(shè)置得太短了節(jié)省不了資源,太長了又減少響應(yīng)速度.真是難辦啊!
這個時候,condition?variable,從天而降,解救了焦頭爛額的你.
你首先定義一個condition?variable.
pthread_cond_t?cond_sum_ready=PTHREAD_COND_INITIALIZER;
t0,t1,t2的代碼僅僅要后面加兩行,像這樣:
add() {pthread_mutex_lock(lock_s);sum++;pthread_mutex_unlock(lock_s);if(sum>=100)pthread_cond_signal(&cond_sum_ready); } 而t3的代碼則是 print {pthread_mutex_lock(lock_s);while(sum<100)pthread_cond_wait(&cond_sum_ready, &lock_s);printf(“sum is over 100!”);sum=0;pthread_mutex_unlock(lock_s);return OK; }注意兩點(diǎn):
1)?在thread_cond_wait()之前,必須先lock相關(guān)聯(lián)的mutex,?由于假如目標(biāo)條件未滿足,pthread_cond_wait()實(shí)際上會unlock該mutex,?然后block,在目標(biāo)條件滿足后再又一次lock該mutex,?然后返回.
2)?為什么是while(sum<100),而不是if(sum<100)??這是由于在pthread_cond_signal()和pthread_cond_wait()返回之間,有時間差,如果在這個時間差內(nèi),還有另外一個線程t4又把sum降低到100下面了,那么t3在pthread_cond_wait()返回之后,顯然應(yīng)該再檢查一遍sum的大小.這就是用?while的用意
線程間的同步技術(shù)。主要以相互排斥鎖和條件變量為主,條件變量和相互排斥所的配合使用能夠非常好的處理對于條件等待的線程間的同步問題
Posix條件變量經(jīng)常使用API:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond);
通常條件變量須要和相互排斥鎖同一時候使用,?利用相互排斥量保護(hù)條件變量;條件的檢測是在相互排斥鎖的保護(hù)下進(jìn)行的。
假設(shè)一個條件為假,一個線程自己主動堵塞,
并釋放等待狀態(tài)改變的相互排斥鎖。假設(shè)還有一個線程改變了條件,它就發(fā)送信號給關(guān)聯(lián)的條件變量,?并喚醒一個或多個等待在該條件變量上的線程,這些線程將又一次獲得相互排斥鎖,又一次評價(jià)條件。假設(shè)將條件變量放到共享內(nèi)存中,?而兩進(jìn)程可共享讀寫這段內(nèi)存,則條件變量能夠被用來實(shí)現(xiàn)兩進(jìn)程間的線程同步。條件變量的使用規(guī)范:
(一)、等待條件代碼 pthread_mutex_lock(&mutex); while (條件為假) pthread_cond_wait(cond, mutex); 改動條件 pthread_mutex_unlock(&mutex);(二)、給條件發(fā)送通知代碼 pthread_mutex_lock(&mutex); 設(shè)置條件為真 pthread_cond_signal(cond); pthread_mutex_unlock(&mutex);注意是while而不是if,原因是在信號的中斷后還能正常執(zhí)行。
解決生產(chǎn)者消費(fèi)者問題(無界緩沖區(qū)):
#include <unistd.h> #include <sys/types.h> #include <pthread.h> #include <semaphore.h>#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h>#define ERR_EXIT(m) \do \{ \perror(m); \exit(EXIT_FAILURE); \} while(0)#define CONSUMERS_COUNT 2 #define PRODUCERS_COUNT 1pthread_mutex_t g_mutex; pthread_cond_t g_cond;pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT];int nready = 0;void *consume(void *arg) {int num = (int)arg;while (1){pthread_mutex_lock(&g_mutex);while (nready == 0){printf("%d begin wait a condtion ...\n", num);pthread_cond_wait(&g_cond, &g_mutex);}printf("%d end wait a condtion ...\n", num);printf("%d begin consume product ...\n", num);--nready;printf("%d end consume product ...\n", num);pthread_mutex_unlock(&g_mutex);sleep(1);}return NULL; }void *produce(void *arg) {int num = (int)arg;while (1){pthread_mutex_lock(&g_mutex);printf("%d begin produce product ...\n", num);++nready;printf("%d end produce product ...\n", num);pthread_cond_signal(&g_cond);printf("%d signal ...\n", num);pthread_mutex_unlock(&g_mutex);sleep(1);}return NULL; }int main(void) {int i;pthread_mutex_init(&g_mutex, NULL);pthread_cond_init(&g_cond, NULL);for (i = 0; i < CONSUMERS_COUNT; i++)pthread_create(&g_thread[i], NULL, consume, (void *)i);sleep(1);for (i = 0; i < PRODUCERS_COUNT; i++)pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void *)i);for (i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; i++)pthread_join(g_thread[i], NULL);pthread_mutex_destroy(&g_mutex);pthread_cond_destroy(&g_cond);return 0; }
轉(zhuǎn)載于:https://www.cnblogs.com/jzssuanfa/p/7081259.html
超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的Linux多线程实践(六)使用Posix条件变量解决生产者消费者问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MYSQL常用函数以及分组操作
- 下一篇: [Linux]结合awk删除hdfs指定