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

陳碩的Blog

用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法

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

前幾天,我發(fā)了一條微博 http://weibo.com/1701018393/A7FrW7ZVd ,質(zhì)疑某本書對(duì) Pthreads 條件變量的封裝是錯(cuò)的,因?yàn)樗鼪]有把 mutex 的 lock()/unlock() 函數(shù)暴露出來,導(dǎo)致無法實(shí)用。后來大家討論的分歧是這個(gè) cond class 是不是通用的條件變量封裝,還是只是一個(gè)特殊的“事件等待器”。作為事件等待器,其實(shí)現(xiàn)也是錯(cuò)的,因?yàn)榇嬖趤G失事件的可能,可以算是初學(xué)者使用條件變量的典型錯(cuò)誤。

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

我在拙作《Linux 多線程服務(wù)端編程:使用 muduo C++ 網(wǎng)絡(luò)庫(kù)》第 2.2 節(jié)總結(jié)了條件變量的使用要點(diǎn):

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

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

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

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

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

1

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

2

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

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

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

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

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

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

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

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

評(píng)論

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

不錯(cuò),假喚醒解釋得對(duì)。  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-09 18:31 askforemore1018

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

版本四中, 在 lock、while之后, wait之前,如果有發(fā) signal的話,也會(huì)丟失 signal吧  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-09 23:49 陳碩

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

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

對(duì)于版本四,只有當(dāng)signal所在線程先獲得鎖修改條件變量再釋放鎖后,此時(shí)wait所在線程獲得鎖正好第一次運(yùn)行到while處,這時(shí)并沒有來得及wait,所以信號(hào)丟失了。  回復(fù)  更多評(píng)論   

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

@askforemore1018
版本八沒有加鎖,當(dāng)在 lock、while之后,wait之前,這個(gè)時(shí)間窗內(nèi)被切換到signal所在線程時(shí),信號(hào)就丟失了。  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-10 01:18 陳碩

@天道酬勤
版本四,在你說的情況下,根本不會(huì)去 wait(),因?yàn)?while 條件不滿足,因此不會(huì)丟信號(hào)。  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-10 09:52 天道酬勤

@陳碩
signal不是發(fā)出信號(hào)了嗎?
  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-10 10:00 陳碩

@ 天道酬勤
你想說啥?  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法[未登錄] 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;
};
有個(gè)問題我始終搞不明白,如果一個(gè)線程先執(zhí)行wait() 這樣這個(gè)線程在被喚醒前,互斥量一直被加鎖,pthread_mutex_lock(&mutex_)。那么signal(),只有互斥量被unlock了,才能調(diào)用pthread_cond_signal(&cond_),那豈不是永遠(yuǎn)也喚醒不了wait()線程了?小弟不才,希望有人幫忙解釋下。  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-10 12:45 Rider

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

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-10 12:47 Rider

@andy
說錯(cuò)了,是條件變量。  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-10 13:34 slade

弱問版本3 spurious wakeup是怎么解釋的啊,為什么會(huì)喚醒多個(gè)線程啊  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法[未登錄] 2013-09-10 14:44 joe

@andy
我記得條件變量wait的內(nèi)部實(shí)現(xiàn)是:在所在進(jìn)場(chǎng)被阻塞前unlock互斥量,這就是為什么wait的參數(shù)需要傳入互斥量的原因了
  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-10 15:19 天道酬勤

@陳碩
@天道酬勤
版本四,在你說的情況下,根本不會(huì)去 wait(),因?yàn)?while 條件不滿足,因此不會(huì)丟信號(hào)。
但是signal已經(jīng)被調(diào)用,信號(hào)不是發(fā)出去了嗎?  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-10 15:48 wingc

@ 天道酬勤
這里要處理的情況是是等待線程和信號(hào)線程的同步,如果等待函數(shù)沒有進(jìn)入同步區(qū),則說明根本沒有等待線程執(zhí)行,就算信號(hào)線程發(fā)出信號(hào)但因?yàn)闆]有等待也就不存在所謂丟失。
這個(gè)和版本一不一樣,版本一因?yàn)樾盘?hào)函數(shù)與等待函數(shù)沒有同步,這等待函數(shù)有可能在進(jìn)入一個(gè)完全無謂的"同步區(qū)"時(shí)而沒有開始等待時(shí),信號(hào)被另一個(gè)線程發(fā)出,因此會(huì)又信號(hào)丟失。
不知道我解釋得對(duì)不對(duì),也不知道你理解了沒有?  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-10 17:24 rtemslinux

@andy
phtread_cond_wait的時(shí)候,這一個(gè)api內(nèi)部實(shí)現(xiàn)有三步
1. unlock
2. wait
3. lock again  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-10 17:27 rtemslinux

@slade

我看wiki說,在多核情況下cond_wait返回,并不代表之前的while條件滿足。
至于pthread_cond_wait為啥會(huì)返回,說是
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.  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2013-09-11 16:22 tb

畫圖比較易懂些了   回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2014-04-22 22:26 xanpeng

試解答思考題。
思考題“如果用兩個(gè) mutex,一個(gè)用于保護(hù)“條件”,另一個(gè)專門用于和 cond 配合 wait(),會(huì)出現(xiàn)什么情況?”的代碼應(yīng)該是按下面這么寫。
* mutex_signaled_用來控制對(duì)signaled_的訪問,mutex_cond_結(jié)合條件變量使用。
* 最大的問題就是原來mutex_unlock+wait是原子的(通過pthread_cond_wait()),現(xiàn)在相當(dāng)于人為拆分成mutex_unlock,wait兩步。這樣可能丟失事件(unlock和wait之間發(fā)生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;
};  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2015-10-30 09:16 @sunjun

加鎖的情況下,情況4不可能有signal丟失。因?yàn)檫@個(gè)waiter在進(jìn)入之前會(huì)獲取mutex鎖,如果條件不滿足cond_wait返回之前會(huì)釋放鎖,而在此之前signal是沒有辦法獲取鎖的,也就是說必須要cond_wait釋放鎖之后,signal才有可能執(zhí)行到@陳碩
  回復(fù)  更多評(píng)論   

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯(cuò)誤做法 2016-01-01 22:10 dodo

版本三為什么錯(cuò)誤  回復(fù)  更多評(píng)論   


只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


<2025年9月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

導(dǎo)航

統(tǒng)計(jì)

常用鏈接

隨筆分類

隨筆檔案

相冊(cè)

搜索

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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精品国产热久久91蜜凸| 一区二区动漫| 欧美一区三区三区高中清蜜桃 | 久久精彩免费视频| 久久久久免费观看| 欧美1区2区| 国产精品av一区二区| 国产日韩av一区二区| 今天的高清视频免费播放成人| 91久久香蕉国产日韩欧美9色| 亚洲婷婷综合久久一本伊一区| 午夜在线a亚洲v天堂网2018| 欧美成人激情视频免费观看| 9色精品在线| 久久久久网站| 国产精品国产三级国产a| 国产亚洲欧洲一区高清在线观看 | 亚洲你懂的在线视频| 久久精品国产第一区二区三区| 免费亚洲一区二区| 一道本一区二区| 久久蜜桃精品| 国产精品视频精品| 亚洲日本久久| 久久免费黄色| 亚洲精品在线电影| 亚洲欧美视频| 亚洲国产欧美一区二区三区同亚洲| 一区二区三区国产在线| 久久久亚洲国产美女国产盗摄| 欧美区在线观看| 尤妮丝一区二区裸体视频| 亚洲一级免费视频| 亚洲国产导航| 久久九九国产| 国产精品一区免费观看| 一本大道久久精品懂色aⅴ| 久久精品国产一区二区三| 亚洲破处大片| 老牛影视一区二区三区| 国产精品一区二区久激情瑜伽| 亚洲精品系列| 欧美a级片网| 欧美一区二区高清| 国产精品网红福利| 亚洲免费中文字幕| 一区二区三区日韩欧美精品| 欧美激情精品久久久久久| 在线观看91精品国产入口| 久久精品国产亚洲a| 亚洲欧美在线免费观看| 国产精品高清在线| 亚洲愉拍自拍另类高清精品| 亚洲精品日韩激情在线电影| 欧美成人黄色小视频| 亚洲欧洲在线一区| 亚洲国产婷婷香蕉久久久久久99 | 久久亚洲私人国产精品va媚药| 国产欧美日韩另类一区| 欧美在线视频a| 亚洲欧美国产三级| 国产日产欧美精品| 久久亚洲国产精品日日av夜夜| 欧美专区日韩视频| 在线精品高清中文字幕| 亚洲国产美女精品久久久久∴| 免费一区视频| 99这里只有久久精品视频| 亚洲激情成人网| 欧美理论电影在线观看| 亚洲一区二区三区成人在线视频精品| 一区二区三区日韩欧美精品| 国产精品人人做人人爽| 久久久久久午夜| 美国十次了思思久久精品导航| 亚洲第一精品影视| 亚洲日韩中文字幕在线播放| 欧美午夜精品电影| 久久久久成人精品| 免费短视频成人日韩| 宅男噜噜噜66一区二区| 午夜激情综合网| 亚洲国产精品日韩| 亚洲午夜精品国产| 一区二区亚洲精品国产| 亚洲一区亚洲二区| 香蕉久久夜色精品国产使用方法| 韩日欧美一区| 亚洲免费观看高清在线观看| 国产精品久久久久久久久搜平片| 久久狠狠亚洲综合| 欧美激情一二三区| 久久久www| 欧美视频一区二区三区四区| 久久艳片www.17c.com| 欧美日韩亚洲一区二区| 久久天堂精品| 欧美午夜激情视频| 欧美福利网址| 国产欧美一区二区三区视频| 欧美成人精品在线视频| 国产精品久久久久久一区二区三区| 久久精品夜色噜噜亚洲aⅴ| 欧美日韩国产首页在线观看| 米奇777超碰欧美日韩亚洲| 国产精品高清免费在线观看| 亚洲第一精品福利| 狠狠入ady亚洲精品| 中日韩男男gay无套| 亚洲国产精品一区二区第一页| 亚洲嫩草精品久久| 亚洲色图自拍| 欧美精品电影在线| 亚洲第一精品久久忘忧草社区| 国产一二三精品| 亚洲综合精品一区二区| 亚洲午夜视频在线观看| 欧美激情精品久久久久久蜜臀| 理论片一区二区在线| 国产午夜精品一区二区三区欧美 | 亚洲激情视频网站| 欧美一区二区久久久| 亚洲欧美中文另类| 欧美三区免费完整视频在线观看| 欧美激情国产高清| 亚洲福利视频网站| 久久婷婷一区| 狼人社综合社区| 狠狠色狠狠色综合人人| 亚洲欧美日韩国产综合在线| 午夜精品久久久99热福利| 国产精品高潮粉嫩av| 一本久久知道综合久久| 亚洲视频免费在线| 欧美色中文字幕| 一本色道久久综合亚洲精品小说| 99国产精品视频免费观看| 欧美精品色综合| 日韩午夜免费| 亚洲欧美色婷婷| 国产日韩亚洲欧美| 久久免费视频在线| 亚洲高清资源综合久久精品| 日韩视频一区二区三区| 欧美三级日本三级少妇99| 亚洲自拍偷拍网址| 久久夜色精品| 99re6热在线精品视频播放速度| 欧美伦理a级免费电影| 中文精品视频| 精品不卡一区| 开心色5月久久精品| 亚洲三级性片| 欧美在线啊v一区| 在线免费观看日本欧美| 欧美激情中文不卡| 亚洲综合视频网| 美女性感视频久久久| 日韩网站在线观看| 国产精品一区一区| 久久综合久久美利坚合众国| 亚洲精品欧美| 亚洲欧美日韩在线| 一区二区三区在线看| 美女尤物久久精品| 一区二区日韩精品| 久久免费视频一区| 一本色道久久综合亚洲91| 国产乱码精品一区二区三区忘忧草 | 久久午夜av| 日韩视频亚洲视频| 久久尤物电影视频在线观看| 一本大道久久a久久精二百| 国产日韩欧美亚洲| 欧美日韩精品福利| 久久久999| 亚洲亚洲精品在线观看| 另类成人小视频在线| 亚洲欧美在线播放| 亚洲欧洲另类国产综合| 国产乱码精品一区二区三区不卡 | 在线亚洲免费| 欧美成在线视频| 欧美在线播放一区二区| 99精品视频网| 亚洲国产精品传媒在线观看| 国产精品高潮久久| 欧美精品三级日韩久久| 久久综合中文色婷婷| 久久成人一区| 亚洲一区二区精品视频| 日韩午夜av在线| 最新国产精品拍自在线播放|