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

陳碩的Blog

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

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

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

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

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

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

對于 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)一個“事件等待器/Waiter”,正確的做法是怎樣的?我的最終答案見 WaiterInMuduo class。“事件等待器”的一種用途是程序啟動時等待初始化完成,也可以直接用 muduo::CountDownLatch 到達(dá)相同的目的,將初值設(shè)為 1 即可。

以下根據(jù)微博上的討論過程給出幾個正確或錯誤的版本,博大家一笑。只要記住 Pthread 的條件變量是邊沿觸發(fā)(edge trigger),即 signal()/broadcast() 只會喚醒已經(jīng)等在 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

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

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

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

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

評論

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# re: 用條件變量實(shí)現(xiàn)事件等待器的正確與錯誤做法 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.  回復(fù)  更多評論   

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

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

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

試解答思考題。
思考題“如果用兩個 mutex,一個用于保護(hù)“條件”,另一個專門用于和 cond 配合 wait(),會出現(xiàn)什么情況?”的代碼應(yīng)該是按下面這么寫。
* mutex_signaled_用來控制對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ù)  更多評論   

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

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

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

版本三為什么錯誤  回復(fù)  更多評論   


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


<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

導(dǎo)航

統(tǒ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>
            久久久亚洲人| 久久久精品国产免费观看同学| 日韩一区二区精品| 亚洲一区二区三区精品在线| 国产精品黄色在线观看| 久久婷婷蜜乳一本欲蜜臀| 日韩视频一区二区三区在线播放| 亚洲承认在线| 国产精品久久久久毛片软件| 久久精品免费播放| 夜色激情一区二区| 亚洲综合日韩在线| 日韩午夜免费| 国内精品视频久久| 欧美日韩在线看| 裸体一区二区三区| 性欧美大战久久久久久久久| 亚洲日本黄色| 蜜桃久久精品乱码一区二区| 亚洲欧美日韩国产成人精品影院| 激情综合亚洲| 国产欧美日本| 国产精品女同互慰在线看| 国产日韩欧美在线| 国产麻豆精品theporn| 国产精品久久久对白| 精品成人一区二区三区| 99精品国产在热久久婷婷| 久久国产精品99久久久久久老狼 | 久久久av毛片精品| 欧美日韩免费视频| 在线精品在线| 亚洲福利视频二区| 亚洲国产一二三| 亚洲成人在线免费| 亚洲欧美美女| 亚洲三级影院| 久久综合999| 麻豆精品91| 国产日韩欧美中文| 亚洲一区二区三区四区视频| 欧美激情按摩| 日韩一级大片在线| 欧美77777| 欧美午夜精彩| 国产精品资源在线观看| 亚洲精品中文字| 一区二区三区四区蜜桃| 亚洲欧美国产va在线影院| 亚洲风情亚aⅴ在线发布| 亚洲电影毛片| 久久天天综合| 精品成人一区二区| 久久免费视频在线观看| 午夜精品福利在线观看| 欧美一区二区三区四区高清| 久久久久国产精品www| 媚黑女一区二区| 国产综合精品| 久久永久免费| 一本在线高清不卡dvd | 久久久久久久高潮| 国一区二区在线观看| 在线观看成人av| 夜夜嗨av色综合久久久综合网| 欧美久久久久| 亚洲一区视频在线| 欧美一区免费| 国产欧美一区二区三区沐欲| 亚洲婷婷综合久久一本伊一区| 久久久天天操| 欧美一区二区在线视频| 国内精品99| 欧美成人免费va影院高清| 亚洲丶国产丶欧美一区二区三区| 久久久一二三| 亚洲美女av在线播放| 亚洲美女免费精品视频在线观看| 欧美日韩精品一区| 一区二区三区在线高清| 免费精品99久久国产综合精品| 久久婷婷亚洲| 99热精品在线| 亚洲欧美日韩国产精品| 精品成人在线| 99www免费人成精品| 国产性天天综合网| 亚洲国产精品ⅴa在线观看| 久久精品国产一区二区三 | 日韩视频在线观看| 夜久久久久久| 一区二区视频免费在线观看| 亚洲大胆女人| 国产欧美精品一区二区三区介绍| 欧美不卡在线| 久久婷婷av| 亚洲午夜精品在线| 久久久国产视频91| 亚洲一区二区三区在线看| 欧美亚洲一区三区| 亚洲免费高清视频| 欧美在线免费一级片| 国产精品亚洲视频| 亚洲一区二区成人在线观看| 午夜精品成人在线视频| 亚洲日韩视频| 亚洲黄页一区| 欧美成人一区二区三区片免费| 国产视频一区欧美| 欧美激情1区2区| 国产欧美日韩视频一区二区三区 | 一区二区三区四区精品| 影音先锋在线一区| 亚洲欧美日韩国产综合在线| 亚洲精品韩国| 久久裸体艺术| 久久gogo国模裸体人体| 欧美日韩精品综合在线| 欧美成人久久| 黑人巨大精品欧美一区二区 | 一本色道久久综合亚洲精品不卡| 国产精品久久一区二区三区| 欧美大片一区二区| 久久久久久久久一区二区| 欧美日韩成人在线播放| 老司机一区二区三区| 午夜精品一区二区三区四区| 亚洲精品视频中文字幕| 久久久久久久999| 久久婷婷人人澡人人喊人人爽| 欧美日韩专区在线| 久久爱www久久做| 国产精品成人一区二区| 亚洲国产精品日韩| 91久久久久久久久| 亚洲一区在线视频| 亚洲午夜一区二区| 欧美日韩中文在线观看| 日韩午夜免费| 亚洲免费在线观看视频| 国产精品对白刺激久久久| 中文有码久久| 亚洲大胆人体视频| 久久久噜噜噜久久狠狠50岁| 美乳少妇欧美精品| 亚洲国产精品传媒在线观看| 免费一级欧美片在线播放| 欧美国产亚洲视频| 99精品福利视频| 欧美视频一区二区在线观看| 亚洲视频精品在线| 久久久激情视频| 亚洲成人在线视频播放| 欧美激情视频一区二区三区免费| 午夜影院日韩| 欧美另类久久久品| 99精品国产99久久久久久福利| 亚洲视频精选| 国产乱码精品一区二区三区不卡 | 亚洲视频狠狠| 久久久综合免费视频| 亚洲电影下载| 欧美日韩国产麻豆| 亚洲伊人一本大道中文字幕| 久久久亚洲国产天美传媒修理工| 亚洲国产精品视频| 欧美性猛交xxxx乱大交退制版| 亚洲在线视频网站| 欧美高清视频在线播放| 亚洲与欧洲av电影| 欧美日韩精品久久久| 亚洲在线成人| 欧美国产日产韩国视频| 国产精品99久久久久久久久久久久 | 亚洲欧美另类国产| 一区二区三区在线不卡| 欧美三区在线视频| 久久久久国产精品一区三寸| 亚洲精品资源美女情侣酒店| 欲香欲色天天天综合和网| 欧美国产日韩一二三区| 亚洲成人资源网| 亚洲欧美日韩在线高清直播| 在线视频国产日韩| 国产精品一香蕉国产线看观看| 免费视频久久| 欧美一区二区视频在线观看2020 | 亚洲专区欧美专区| 美女主播精品视频一二三四| 亚洲一区成人| 亚洲三级网站| 国产专区精品视频| 国产精品人人爽人人做我的可爱| 久久天天狠狠| 久久精品视频播放| 性欧美xxxx大乳国产app| 这里是久久伊人| 一本久道久久综合狠狠爱| 亚洲福利电影| 亚洲国产欧美在线|