青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

陳碩的Blog

用條件變量實現事件等待器的正確與錯誤做法

TL;DR 如果你能一眼看出 https://gist.github.com/chenshuo/6430925 中的那 8 個 Waiter classes 哪些是對的哪些是錯的,本文就不必看了。

前幾天,我發了一條微博 http://weibo.com/1701018393/A7FrW7ZVd ,質疑某本書對 Pthreads 條件變量的封裝是錯的,因為它沒有把 mutex 的 lock()/unlock() 函數暴露出來,導致無法實用。后來大家討論的分歧是這個 cond class 是不是通用的條件變量封裝,還是只是一個特殊的“事件等待器”。作為事件等待器,其實現也是錯的,因為存在丟失事件的可能,可以算是初學者使用條件變量的典型錯誤。

本文的代碼位于 recipes/thread/test/Waiter_test.cc,這里提到的某書的版本相當于 Waiter1 class。

我在拙作《Linux 多線程服務端編程:使用 muduo C++ 網絡庫》第 2.2 節總結了條件變量的使用要點:

條件變量只有一種正確使用的方式,幾乎不可能用錯。對于 wait 端:
1. 必須與 mutex 一起使用,該布爾表達式的讀寫需受此 mutex 保護。
2. 在 mutex 已上鎖的時候才能調用 wait()。
3. 把判斷布爾條件和 wait() 放到 while 循環中。

對于 signal/broadcast 端:
1. 不一定要在 mutex 已上鎖的情況下調用 signal (理論上)。
2. 在 signal 之前一般要修改布爾表達式。
3. 修改布爾表達式通常要用 mutex 保護(至少用作 full memory barrier)。
4. 注意區分 signal 與 broadcast:“broadcast 通常用于表明狀態變化,signal 通常用于表示資源可用。(broadcast should generally be used to indicate state change rather than resource availability。)”

如果用條件變量來實現一個“事件等待器/Waiter”,正確的做法是怎樣的?我的最終答案見 WaiterInMuduo class。“事件等待器”的一種用途是程序啟動時等待初始化完成,也可以直接用 muduo::CountDownLatch 到達相同的目的,將初值設為 1 即可。

以下根據微博上的討論過程給出幾個正確或錯誤的版本,博大家一笑。只要記住 Pthread 的條件變量是邊沿觸發(edge trigger),即 signal()/broadcast() 只會喚醒已經等在 wait() 上的線程(s),我們在編碼時必須要考慮 signal() 早于 wait() 的可能,那么就很容易判斷以下各個版本的正誤了。代碼見 recipes/thread/test/Waiter_test.cc

版本一:錯誤。某書上的原始版,有丟失事件的可能。

1

版本二:錯誤。lock() 之后再 signal(),同樣有丟失事件的可能。

2

版本三:錯誤。引入了 bool signaled_; 條件,但沒有正確處理 spurious wakeup。

版本四五六:正確。僅限 single waiter 使用。

版本七:最佳??晒?multiple waiters 使用。

版本八:錯誤。存在 data race,且有丟失事件的可能。理由見 http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex

總結:使用條件變量,調用 signal() 的時候無法知道是否已經有線程等待在 wait() 上。因此一般總是要先修改“條件”,使其為 true,再調用 signal();這樣 wait 線程先檢查“條件”,只有當條件不成立時才去 wait(),避免了丟事件的可能。換言之,通過使用“條件”,將邊沿觸發(edge trigger)改為電平觸發(level trigger)。這里“修改條件”和“檢查條件”都必須在 mutex 保護下進行,而且這個 mutex 必須用于配合 wait()。

思考題:如果用兩個 mutex,一個用于保護“條件”,另一個專門用于和 cond 配合 wait(),會出現什么情況?

最后注明一點,http://stackoverflow.com/questions/6419117/signal-and-unlock-order 這篇帖子里對 spurious wakeup 的解釋是錯的,spurious wakeup 指的是一次 signal() 調用喚醒兩個或以上 wait()ing 的線程,或者沒有調用 signal() 卻有線程從 wait() 返回。manpage 里對 Pthreads 系列函數的介紹非常到位,值得細讀。

posted on 2013-09-09 03:01 陳碩 閱讀(14275) 評論(21)  編輯 收藏 引用

評論

# re: 用條件變量實現事件等待器的正確與錯誤做法[未登錄] 2013-09-09 11:47 春秋十二月

不錯,假喚醒解釋得對。  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-09 18:31 askforemore1018

"因此一般總是要先修改條件,使其為 true,再調用 signal();這樣 wait 線程先檢查條件,只有當條件不成立時才去 wait(),避免了丟事件的可能"

版本四中, 在 lock、while之后, wait之前,如果有發 signal的話,也會丟失 signal吧  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-09 23:49 陳碩

@askforemore1018
在你說的這種情況下,版本八有可能丟,版本四不可能丟,想想為什么。  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法[未登錄] 2013-09-10 01:02 天道酬勤

對于版本四,只有當signal所在線程先獲得鎖修改條件變量再釋放鎖后,此時wait所在線程獲得鎖正好第一次運行到while處,這時并沒有來得及wait,所以信號丟失了。  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法[未登錄] 2013-09-10 01:06 天道酬勤

@askforemore1018
版本八沒有加鎖,當在 lock、while之后,wait之前,這個時間窗內被切換到signal所在線程時,信號就丟失了。  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-10 01:18 陳碩

@天道酬勤
版本四,在你說的情況下,根本不會去 wait(),因為 while 條件不滿足,因此不會丟信號。  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-10 09:52 天道酬勤

@陳碩
signal不是發出信號了嗎?
  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-10 10:00 陳碩

@ 天道酬勤
你想說啥?  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法[未登錄] 2013-09-10 10:56 andy

class Waiter6 : private WaiterBase
{
public:
void wait()
{
CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
while (!signaled_)
{
CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_));
}
CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
}

void signal()
{
CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
CHECK_SUCCESS(pthread_cond_signal(&cond_));
signaled_ = true;
CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
}

private:
bool signaled_ = false;
};
有個問題我始終搞不明白,如果一個線程先執行wait() 這樣這個線程在被喚醒前,互斥量一直被加鎖,pthread_mutex_lock(&mutex_)。那么signal(),只有互斥量被unlock了,才能調用pthread_cond_signal(&cond_),那豈不是永遠也喚醒不了wait()線程了?小弟不才,希望有人幫忙解釋下。  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-10 12:45 Rider

@andy
感覺你不知道信號量這個概念,最好能先查下手冊或者翻下OS的書再提問,別人的時間也是時間啊。wait的時候是失去鎖的,并且這一步是原子的。
  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-10 12:47 Rider

@andy
說錯了,是條件變量。  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-10 13:34 slade

弱問版本3 spurious wakeup是怎么解釋的啊,為什么會喚醒多個線程啊  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法[未登錄] 2013-09-10 14:44 joe

@andy
我記得條件變量wait的內部實現是:在所在進場被阻塞前unlock互斥量,這就是為什么wait的參數需要傳入互斥量的原因了
  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-10 15:19 天道酬勤

@陳碩
@天道酬勤
版本四,在你說的情況下,根本不會去 wait(),因為 while 條件不滿足,因此不會丟信號。
但是signal已經被調用,信號不是發出去了嗎?  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-10 15:48 wingc

@ 天道酬勤
這里要處理的情況是是等待線程和信號線程的同步,如果等待函數沒有進入同步區,則說明根本沒有等待線程執行,就算信號線程發出信號但因為沒有等待也就不存在所謂丟失。
這個和版本一不一樣,版本一因為信號函數與等待函數沒有同步,這等待函數有可能在進入一個完全無謂的"同步區"時而沒有開始等待時,信號被另一個線程發出,因此會又信號丟失。
不知道我解釋得對不對,也不知道你理解了沒有?  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-10 17:24 rtemslinux

@andy
phtread_cond_wait的時候,這一個api內部實現有三步
1. unlock
2. wait
3. lock again  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-10 17:27 rtemslinux

@slade

我看wiki說,在多核情況下cond_wait返回,并不代表之前的while條件滿足。
至于pthread_cond_wait為啥會返回,說是
Spurious wakeups may sound strange, but on some multiprocessor systems, making condition wakeup completely predictable might substantially slow all condition variable operations. The race conditions that cause spurious wakeups should be considered rare.  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2013-09-11 16:22 tb

畫圖比較易懂些了   回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2014-04-22 22:26 xanpeng

試解答思考題。
思考題“如果用兩個 mutex,一個用于保護“條件”,另一個專門用于和 cond 配合 wait(),會出現什么情況?”的代碼應該是按下面這么寫。
* mutex_signaled_用來控制對signaled_的訪問,mutex_cond_結合條件變量使用。
* 最大的問題就是原來mutex_unlock+wait是原子的(通過pthread_cond_wait()),現在相當于人為拆分成mutex_unlock,wait兩步。這樣可能丟失事件(unlock和wait之間發生signal)。

class Waiter9 : private WaiterBase
{
public:
void wait()
{
CHECK_SUCCESS(pthread_mutex_lock(&mutex_signaled_));
while (!signaled_)
{
CHECK_SUCCESS(pthread_mutex_unlock(&mutex_signaled_)); // 第一步
CHECK_SUCCESS(pthread_mutex_lock(&mutex_cond_));
CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_cond_)); // 第二步
CHECK_SUCCESS(pthread_mutex_unlock(&mutex_cond_));
CHECK_SUCCESS(pthread_mutex_lock(&mutex_signaled_));
}
CHECK_SUCCESS(pthread_mutex_unlock(&mutex_signaled_));
}

void broadcast()
{
CHECK_SUCCESS(pthread_mutex_lock(&mutex_signaled_));
signaled_ = true;
CHECK_SUCCESS(pthread_mutex_unlock(&mutex_signaled_));

CHECK_SUCCESS(pthread_mutex_lock(&mutex_cond_)); // 可以不要
CHECK_SUCCESS(pthread_cond_broadcast(&cond_));
CHECK_SUCCESS(pthread_mutex_unlock(&mutex_cond_));
}

private:
bool signaled_ = false;
};  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2015-10-30 09:16 @sunjun

加鎖的情況下,情況4不可能有signal丟失。因為這個waiter在進入之前會獲取mutex鎖,如果條件不滿足cond_wait返回之前會釋放鎖,而在此之前signal是沒有辦法獲取鎖的,也就是說必須要cond_wait釋放鎖之后,signal才有可能執行到@陳碩
  回復  更多評論   

# re: 用條件變量實現事件等待器的正確與錯誤做法 2016-01-01 22:10 dodo

版本三為什么錯誤  回復  更多評論   

<2015年10月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

導航

統計

常用鏈接

隨筆分類

隨筆檔案

相冊

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲精品一区在线观看香蕉| 欧美视频一区二区三区…| 欧美国产专区| 亚洲第一区中文99精品| 欧美一区亚洲一区| 久久精品动漫| 猫咪成人在线观看| 欧美顶级少妇做爰| 最近中文字幕日韩精品 | 欧美精品一线| 欧美高清视频一区二区| 欧美精品在线一区二区| 欧美日韩欧美一区二区| 国产精品天美传媒入口| 国产一区二区电影在线观看| 亚洲大片一区二区三区| 日韩小视频在线观看专区| 亚洲欧美日本日韩| 久久伊人精品天天| 亚洲第一在线综合网站| 亚洲一区视频在线| 狼狼综合久久久久综合网| 欧美性生交xxxxx久久久| 黄色成人av网站| 中文网丁香综合网| 久久人人97超碰精品888| 亚洲国产三级网| 欧美在线观看你懂的| 欧美精品久久一区| 精品不卡一区二区三区| 亚洲一区中文字幕在线观看| 久久色在线观看| 在线午夜精品自拍| 免费久久99精品国产自在现线| 国产精品一区二区三区久久 | 韩日午夜在线资源一区二区| 夜夜嗨av色一区二区不卡| 久久久国际精品| 一本色道婷婷久久欧美| 免费在线播放第一区高清av| 国产精品日日摸夜夜添夜夜av | 国内精品久久久久影院色 | 国产精品高潮呻吟视频| 亚洲激情网站免费观看| 久久大逼视频| 亚洲无人区一区| 欧美美女喷水视频| 最近看过的日韩成人| 玖玖在线精品| 欧美在线观看视频| 国内精品久久久久久 | 精品成人国产在线观看男人呻吟| 亚洲专区在线视频| 一区二区三区精品国产| 欧美日在线观看| 亚洲精品欧美| 欧美电影免费观看| 美女网站在线免费欧美精品| 在线观看欧美视频| 男女av一区三区二区色多| 久久久精品一区| 在线不卡视频| 欧美成人一区二区在线| 另类亚洲自拍| 亚洲第一页自拍| 欧美国产欧美亚州国产日韩mv天天看完整| 欧美影院在线| 精品av久久707| 欧美激情国产日韩精品一区18| 久久这里只有| 亚洲精品中文字| 99人久久精品视频最新地址| 国产精品wwwwww| 久久精品在线观看| 久久精品中文| 亚洲精品中文字幕有码专区| 在线一区二区三区做爰视频网站 | 国产精品久久| 久久精品30| 久久三级视频| 亚洲少妇诱惑| 欧美亚洲一区二区三区| 1024成人| 99国产精品99久久久久久粉嫩| 国产精品激情av在线播放| 久久国产日本精品| 久久人人超碰| 亚洲午夜激情网站| 欧美一区二区精品| 亚洲国产精品女人久久久| 亚洲毛片一区二区| 国产女主播一区| 欧美激情按摩| 国产乱码精品一区二区三区不卡 | 蜜臀久久99精品久久久久久9| 99精品国产高清一区二区| 亚洲色图综合久久| 永久免费毛片在线播放不卡| 日韩视频在线免费观看| 久久久久久久一区二区三区| 亚洲免费高清| 欧美在线视频a| 亚洲看片免费| 久久爱www.| 香蕉精品999视频一区二区| 久久一本综合频道| 午夜一级在线看亚洲| 欧美二区乱c少妇| 久久久久久久久久久久久9999| 欧美激情1区2区3区| 久久久久久久国产| 国产精品豆花视频| 99国产精品一区| 免费看成人av| 国产精品五月天| 亚洲人成网站777色婷婷| 国产亚洲欧美色| 亚洲私人影院在线观看| 亚洲国产高清视频| 欧美中文日韩| 欧美综合国产| 国产精品嫩草影院一区二区| 最新国产乱人伦偷精品免费网站| 国内揄拍国内精品久久| 亚洲一区二区在线看| 亚洲精品乱码久久久久久日本蜜臀| 先锋影音国产精品| 午夜亚洲一区| 国产精品午夜av在线| 亚洲天堂成人| 亚洲中无吗在线| 国产精品国产三级欧美二区| 99国内精品| 亚洲影院免费观看| 国产精品盗摄一区二区三区| 一区二区免费看| 亚洲一区二区三区久久| 欧美图区在线视频| 一区二区欧美日韩| 亚洲欧美欧美一区二区三区| 国产精品播放| 亚洲永久在线观看| 久久电影一区| 亚洲成色最大综合在线| 免费影视亚洲| 日韩午夜在线| 欧美一二区视频| 国产精品视屏| 久久九九免费视频| 欧美电影美腿模特1979在线看 | 在线观看91精品国产入口| 久久精品国产亚洲精品| 麻豆国产va免费精品高清在线| 在线国产精品播放| 欧美va天堂在线| 99精品热视频| 久久福利毛片| 在线播放日韩专区| 欧美激情视频一区二区三区在线播放 | 欧美四级在线观看| 午夜精品久久久久久久男人的天堂| 欧美专区日韩专区| 亚洲国产欧美一区二区三区同亚洲| 美女网站在线免费欧美精品| 亚洲靠逼com| 久久精品二区三区| 亚洲激情电影中文字幕| 欧美日韩一卡二卡| 欧美自拍丝袜亚洲| 亚洲精品一区二区三区婷婷月| 亚洲专区一区二区三区| 尤物九九久久国产精品的分类| 欧美激情一区| 欧美中文字幕第一页| 亚洲日本va午夜在线影院| 欧美一级淫片播放口| 亚洲国产精品福利| 国产精品久久久久久亚洲调教 | 性久久久久久久久| 伊人一区二区三区久久精品| 欧美精品一区在线| 久久国产精品久久精品国产| 亚洲国产小视频在线观看| 午夜伦欧美伦电影理论片| 亚洲国产精品一区| 国产欧美精品| 欧美日韩理论| 麻豆国产va免费精品高清在线| 一本久久a久久免费精品不卡| 老司机午夜精品视频| 午夜一区二区三区不卡视频| 亚洲精品在线免费观看视频| 国产一区二区三区免费观看| 欧美三级在线| 欧美精品乱码久久久久久按摩 | 国产伦精品一区二区三区| 欧美国产日产韩国视频| 久久精品亚洲一区| 亚洲男女毛片无遮挡| 99国产成+人+综合+亚洲欧美|