日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

windows环境下封装条件wait和signal

發(fā)布時間:2025/3/15 windows 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 windows环境下封装条件wait和signal 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

linux 環(huán)境有提供好的pthread_cond_wait() 和 phread_signal()、pthread_broadcast()

windows需要自己封裝,利用semophore控制線程等待和釋放,先簡單談一下設(shè)計好后api該

如何使用。

假設(shè)我們封裝好條件變量等待函數(shù)名字叫做wait(Mutex& mutex),Mutex是之前我們封裝的

條件變量,文章最下邊會給出這些文件的下載地址,在這里讀者當(dāng)做linux 的mutex即可。我們

封裝的釋放函數(shù)為signal(),廣播函數(shù)為broadcast。

判斷等待條件變量和邏輯處理如下:

Lock(mutex);

while(條件不滿足)

{

?  ?wait(mutex);

}

todo...;

UnLock(mutex);

?

激活條件變量如下:

Lock(mutex);

  todo ...; 

  if(條件滿足)

  {

    signal();/broadcast();

  } 

signal();

UnLock(mutex);

?

Condition?是我們封裝的條件變量類

這是封裝好api后調(diào)用規(guī)則,那么先考慮wait內(nèi)部的基本形式

void?Condition::wait(Mutex &mutex)

{

  //1 Condition?類中表示阻塞線程數(shù) 

  mblocked ++;

  //2 解鎖,釋放互斥量

  UnLock(mutex);

  //3 阻塞等待?mQueue為信號量

 ??res = WaitForSingleObject(reinterpret_cast<HANDLE>(mQueue), INFINITE);

  ?//4 做一些判斷和邏輯處理

  //5 加鎖

? Lock(mutex);

}

wait內(nèi)部記錄一個阻塞的線程數(shù)mblocked,mblocked 是我們封裝Condition類的成員變量,

然后釋放外部的互斥量,然后調(diào)用阻塞函數(shù),等待signal喚醒。

當(dāng)WaitForSingleObject獲取信號后會繼續(xù)執(zhí)行,做一些邏輯判斷,最后將mutex鎖住。

這里用到的mQueue是一個信號量,用信號量可以接受多個喚醒和控制線程喚醒數(shù)量。

?

下面是條件變量釋放的函數(shù),我們先做只是放一個條件變量的api

void Condition::signal()

{

  //1阻塞的線程減少

  mblocked --;

  //2將激活的信號個數(shù)設(shè)置為1

  signals = 1;

  //3

  if (signals)

  {

    //釋放信號量
    res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mQueue), signals, 0);
    ASSERT(res);
  }

}

?

先不要著急往下寫,考慮下這么做真的合適么?

首先之前設(shè)計過外部調(diào)用

  if(條件滿足)

  {

    signal();/broadcast();

  } 

?

這個只要條件滿足就可以激活,所以我們只用mblocked表示阻塞線程數(shù)是不夠的,當(dāng)信號量被激活很多沒有被消耗的情況下

就需要統(tǒng)計當(dāng)前可用的資源數(shù),那么就在Condition類添加mWait表示當(dāng)前可用的信號量個數(shù)。除此之外,考慮這樣一種情況,

當(dāng)條件不滿足的時候 線程A調(diào)用void wait(Mutex &mutex)函數(shù),wait函數(shù)先解鎖再阻塞,對應(yīng)wait中第2,3步驟。而另一個

線程B當(dāng)條件滿足時調(diào)用 signal函數(shù)激活之前阻塞的線程A,對應(yīng)signal函數(shù)中第3步 。原阻塞線程A因為捕獲到信號量,所以

一次走到wait中第4、5步。由于第4和第5步之間沒有加鎖保護,所以這一階段用到的類的成員變量都是不安全的。所以在第3

和第4之間加一個互斥鎖,第5步之后釋放這個互斥鎖。同樣的道理,為了避免此時signal內(nèi)部調(diào)用類的成員變量造成數(shù)據(jù)不一致

所以signal內(nèi)部也需要加鎖,在signal內(nèi)部第1步之前加鎖,第3步之后解鎖,或者第3步之前解鎖都可以。我覺得在第三步之前

釋放會好一些,在釋放信號量之前解鎖,避免死鎖。所以添加一個成員變量mMutex

用于部分代碼互斥。

那么改良后我們的函數(shù)如下:

void Condition::wait(Mutex& mutex) {#ifndef WIN32int ret = pthread_cond_wait(&mId, mutex.getId());ASSERT(ret == 0);#else//1mBlocked++;
//2mutex.unlock();int res = 0;
     //3res = WaitForSingleObject(reinterpret_cast<HANDLE>(mQueue), INFINITE);ASSERT(res == WAIT_OBJECT_0);//用于暫時存儲mWaiting的數(shù)值unsigned wasWaiting = 0;//4res = WaitForSingleObject(reinterpret_cast<HANDLE>(mMutex), INFINITE);ASSERT(res == WAIT_OBJECT_0);wasWaiting = mWaiting;//5res = ReleaseMutex(reinterpret_cast<HANDLE>(mMutex));ASSERT(res);//6mutex.lock(); #endif }

  步驟也做了相應(yīng)的調(diào)整。

void Condition::signal () { #ifndef WIN32int ret = pthread_cond_signal(&mId);ASSERT(ret == 0); #elseunsigned signals = 0;int res = 0;
  //1res
= WaitForSingleObject(reinterpret_cast<HANDLE>(mMutex), INFINITE);ASSERT(res == WAIT_OBJECT_0);//2if (mWaiting != 0) {if (mBlocked == 0){res = ReleaseMutex(reinterpret_cast<HANDLE>(mMutex));ASSERT(res);return;}++mWaiting;--mBlocked;signals = 1;}else{signals = mWaiting = 1;--mBlocked;}//3res = ReleaseMutex(reinterpret_cast<HANDLE>(mMutex));ASSERT(res);//4if (signals){res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mQueue), signals, 0);ASSERT(res);} #endif }

?

改良后更新了步驟,注釋的就是步驟,方便接下來討論這兩段代碼的隱患,因為僅僅這些還不夠。目前現(xiàn)總結(jié)下mMutex作用:

1mMutex用于signal函數(shù)內(nèi)部和wait函數(shù) 獲取信號量之后的代碼互斥,保護類的常用變量。

2當(dāng)不同的線程調(diào)用wait等待后獲得激活時,mMutex保證獲得信號量之后的操作是互斥的,安全的。

由于調(diào)用wait函數(shù)之前需要加外部的互斥鎖,所以不同的線程調(diào)用wai函數(shù)時第一步的mBlocked++是互斥的,不會出錯。

唯一有可能出錯的是那種情況呢?

就是當(dāng)signal發(fā)出信號后,當(dāng)前有一個因為調(diào)用wait阻塞的線程A捕獲到該信號,進入第四步,修改或者訪問mBlocked變量的值,

與此同時有線程A調(diào)用wait函數(shù),此時會進入wait內(nèi)部第一步mBlocked++,多線程修改和讀取mBlocked會造成數(shù)據(jù)混亂,

所以此時需要在第一步之前加鎖,第2步之前解鎖,因此添加單個信號量mGate,用于控制當(dāng)有線程處于解鎖狀態(tài)處理mBlocked等

類成員時,其他線程進入wait修改mBlocked值。

這個res = WaitForSingleObject(reinterpret_cast<HANDLE>(mGate), INFINITE);可以放在wait函數(shù)第4步之后,當(dāng)?shù)?步獲得互斥

資源后,阻塞等待獲取mGate信號,如果沒獲得需要等待別的線程釋放mGate,如果此時mGate不被釋放造成mMutex死鎖。所以

別的線程中先調(diào)用 WaitForSingleObject(reinterpret_cast<HANDLE>(mGate), INFINITE);后調(diào)用WaitForSingleObject mMutex會造成

死鎖。需要特別注意。如果規(guī)避了這一點,那么就可以避免死鎖。所有情況都對mGate互斥訪問并不友好,出現(xiàn)之前討論的情況只有一種:

就是當(dāng)前應(yīng)用程序中至少有一個線程處于等待,而signal釋放信號后,某一個等待的線程繼續(xù)執(zhí)行4后面的操作,外界有新的線程調(diào)用wait時

修改mBlocked會出錯。所以只需要在signal函數(shù)中判斷當(dāng)mWaiting數(shù)量為0時對mGate加鎖,mWait根據(jù)不同情況進行對mGate進行釋放。

修改后的代碼如下:

?

先封裝一個小函數(shù):

void Condition::enterWait () {int res = 0;res = WaitForSingleObject(reinterpret_cast<HANDLE>(mGate), INFINITE);ASSERT(res == WAIT_OBJECT_0);++mBlocked;res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mGate), 1, 0);ASSERT(res); }

?

對mBlocked起到保護作用

void Condition::wait(Mutex& mutex) { #ifndef WIN32int ret = pthread_cond_wait(&mId, mutex.getId());ASSERT(ret == 0); #else//1
  enterWait();//2mutex.unlock();
int res = 0;
  //3res
= WaitForSingleObject(reinterpret_cast<HANDLE>(mQueue), INFINITE);ASSERT(res == WAIT_OBJECT_0);unsigned wasWaiting = 0;unsigned wasGone = 0;//4res = WaitForSingleObject(reinterpret_cast<HANDLE>(mMutex), INFINITE);ASSERT(res == WAIT_OBJECT_0);wasWaiting = mWaiting;wasGone = mGone;
  //signal釋放資源后,mWaiting 至少為1
if (wasWaiting != 0){
    //判斷mWaiting 數(shù)量為1
if (--mWaiting == 0){
       //如果當(dāng)前沒有阻塞線程則釋放mGate
if (mBlocked != 0){res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mGate), 1, 0); // open mGate ASSERT(res);wasWaiting = 0;}}}//5res = ReleaseMutex(reinterpret_cast<HANDLE>(mMutex));ASSERT(res);//6mutex.lock(); #endif }

?

?

對應(yīng)的signal函數(shù):

?

void Condition::signal () { #ifndef WIN32int ret = pthread_cond_signal(&mId);ASSERT(ret == 0); #elseunsigned signals = 0;int res = 0;
  //1res
= WaitForSingleObject(reinterpret_cast<HANDLE>(mMutex), INFINITE);ASSERT(res == WAIT_OBJECT_0);if (mWaiting != 0) {
    //當(dāng)前有空閑的信號量并且沒由阻塞的線程
if (mBlocked == 0){res = ReleaseMutex(reinterpret_cast<HANDLE>(mMutex));ASSERT(res);return;}//如果由阻塞的線程,那么阻塞數(shù)量--++mWaiting;--mBlocked;signals = 1;}else{//2當(dāng)空閑的信號量為0時,互斥獲得mGate
     res
= WaitForSingleObject(reinterpret_cast<HANDLE>(mGate), INFINITE);ASSERT(res == WAIT_OBJECT_0);
     //3
if (mBlocked ){//如果當(dāng)前有線程阻塞那么更新計數(shù)signals = mWaiting = 1;--mBlocked;}else{
       //由于用戶外部不判斷條件是否成立多次調(diào)動signal,此處不處理直接釋放mGateres
= ReleaseSemaphore(reinterpret_cast<HANDLE>(mGate), 1, 0);ASSERT(res);}}
  //4res
= ReleaseMutex(reinterpret_cast<HANDLE>(mMutex));ASSERT(res);
  //5
if (signals){res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mQueue), signals, 0);ASSERT(res);} #endif }

?

到目前為止,對于共享對象的保護和同步都做的比較完善了,還要注意一個問題就是虛假喚醒。這是

操作系統(tǒng)可能出現(xiàn)的一種情況,所以需要添加虛假喚醒的邏輯用mGone成員變量表示出錯的或是虛假喚醒的線程數(shù)

最終代碼如下:

void Condition::wait(Mutex& mutex) { #ifndef WIN32int ret = pthread_cond_wait(&mId, mutex.getId());ASSERT(ret == 0); #elseenterWait();mutex.unlock();int res = 0;res = WaitForSingleObject(reinterpret_cast<HANDLE>(mQueue), INFINITE);ASSERT(res == WAIT_OBJECT_0);unsigned wasWaiting = 0;unsigned wasGone = 0;res = WaitForSingleObject(reinterpret_cast<HANDLE>(mMutex), INFINITE);ASSERT(res == WAIT_OBJECT_0);wasWaiting = mWaiting;wasGone = mGone;if (wasWaiting != 0){if (--mWaiting == 0){if (mBlocked != 0){res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mGate), 1, 0); // open mGate ASSERT(res);wasWaiting = 0;}else if (mGone != 0){mGone = 0;}}}else if (++mGone == (ULONG_MAX / 2)){res = WaitForSingleObject(reinterpret_cast<HANDLE>(mGate), INFINITE);ASSERT(res == WAIT_OBJECT_0);mBlocked -= mGone;res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mGate), 1, 0);ASSERT(res);mGone = 0;}res = ReleaseMutex(reinterpret_cast<HANDLE>(mMutex));ASSERT(res);if (wasWaiting == 1){for (; wasGone; --wasGone){res = WaitForSingleObject(reinterpret_cast<HANDLE>(mQueue), INFINITE);ASSERT(res == WAIT_OBJECT_0);}res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mGate), 1, 0);ASSERT(res);}mutex.lock(); #endif }

?

wait部分添加了mGone的處理,當(dāng)mWaiting數(shù)量為0進入

res = WaitForSingleObject(reinterpret_cast<HANDLE>(mMutex), INFINITE);
需要對mGone++表示虛假喚醒的線程數(shù)量 if (++mGone == (ULONG_MAX / 2)){res = WaitForSingleObject(reinterpret_cast<HANDLE>(mGate), INFINITE);ASSERT(res == WAIT_OBJECT_0);mBlocked -= mGone;res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mGate), 1, 0);ASSERT(res);mGone = 0; }
通過mGate對mBlocked保護起來,當(dāng)喚醒的個數(shù)超過指定值會把多余的mblocked去掉并且把
虛假喚醒數(shù)量置空。舉個例子,當(dāng)mBLocked為1時該線程被虛假喚醒,那么mGone變?yōu)?,由于是
虛假喚醒,用戶在外部調(diào)用wait函數(shù)時通過while循環(huán)判斷條件不滿足再次進入wait中enterGate
函數(shù)對mBlocked自增,此時mBlocked數(shù)量為2,所以當(dāng)冗余的mBlocked超過指定值,就回去掉
這些mBlocked并將mGone置空。


if (wasWaiting == 1) {for (; wasGone; --wasGone){res = WaitForSingleObject(reinterpret_cast<HANDLE>(mQueue), INFINITE);ASSERT(res == WAIT_OBJECT_0);}res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mGate), 1, 0);ASSERT(res); } 該函數(shù)判斷Condation類的mWating變量有1變?yōu)?,并且阻塞的線程數(shù)為0,因為如果用戶沒有在外邊調(diào)用while
判斷條件導(dǎo)致虛假喚醒引起邏輯錯誤,所以為了起到保護作用對那些因為虛假喚醒錯過的信號進行資源占用,
直到信號量都被釋放后才進入mGate釋放。舉一個例子
如果外部調(diào)用
Lock(mutex);
if(條件不滿足)
{
  wait(mutex);
  
} //邏輯處理
    ...
UnLock(mutex);
當(dāng)wait執(zhí)行退出后會執(zhí)行邏輯,而沒有while判斷條件是否真的滿足。所以我們要對信號量進行控制,保證信號量
數(shù)量正確。并且和mBlocked,mWait,等一致。
下面是signal函數(shù)最終版本 void Condition::signal () { #ifndef WIN32int ret = pthread_cond_signal(&mId);ASSERT(ret == 0); #elseunsigned signals = 0;int res = 0;res = WaitForSingleObject(reinterpret_cast<HANDLE>(mMutex), INFINITE);ASSERT(res == WAIT_OBJECT_0);if (mWaiting != 0) {if (mBlocked == 0){res = ReleaseMutex(reinterpret_cast<HANDLE>(mMutex));ASSERT(res);return;}++mWaiting;--mBlocked;signals = 1;}else{res = WaitForSingleObject(reinterpret_cast<HANDLE>(mGate), INFINITE);ASSERT(res == WAIT_OBJECT_0);if (mBlocked > mGone){if (mGone != 0){mBlocked -= mGone;mGone = 0;}signals = mWaiting = 1;--mBlocked;}else{res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mGate), 1, 0);ASSERT(res);}}res = ReleaseMutex(reinterpret_cast<HANDLE>(mMutex));ASSERT(res);if (signals){res = ReleaseSemaphore(reinterpret_cast<HANDLE>(mQueue), signals, 0);ASSERT(res);} #endif }

?


同樣的道理 if (mBlocked > mGone){if (mGone != 0){mBlocked -= mGone;mGone = 0;}signals = mWaiting = 1; --mBlocked; }
這個邏輯就是處理當(dāng)虛假喚醒的mBlocked和mGone等數(shù)據(jù)準確性。
因為如果是虛假喚醒,用戶通過while(條件不滿足)這個方式繼續(xù)調(diào)用wait
會導(dǎo)致mBlocked++,假設(shè)就一個線程處于阻塞并且因為虛假喚醒通過while循環(huán)
重新調(diào)用wait函數(shù),而此時mGone比mBlocked小1,所以mBlocked - mGone就是
更新差值給mBlocked,這是真正的處于阻塞的線程數(shù)量。

下面是代碼下載地址:

http://download.csdn.net/detail/secondtonone1/9658645

代碼效果測試截圖:



謝謝關(guān)注我的微信公眾號:

?

轉(zhuǎn)載于:https://www.cnblogs.com/secondtonone1/p/5976832.html

總結(jié)

以上是生活随笔為你收集整理的windows环境下封装条件wait和signal的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 麻豆婷婷 | 男人插女人的网站 | 无码人妻丰满熟妇精品区 | 天堂在线中文资源 | 麻豆视屏| 亚洲综合成人在线 | 日韩中文字幕免费在线观看 | 久久中文字幕在线观看 | 亚洲性生活视频 | 日韩在线精品强乱中文字幕 | 在线免费黄网 | 8090理论片午夜理伦片 | 大桥未久恸哭の女教师 | 日本成人精品在线 | 五月激情综合婷婷 | 日韩免费一区二区三区 | 国产理论片在线观看 | 极品人妻一区二区 | 亚洲一区国产精品 | 色综合久久天天综合网 | 欧美成人激情 | 亚洲国产中文字幕在线观看 | 四虎影院在线观看免费 | 国产精品国产三级国产a | 日韩av片免费观看 | 国产专区在线视频 | 农村妇女愉情三级 | av免费精品| 97一级片| 免费操| 欧美无砖专区免费 | 欧美一级淫片 | 欧美另类老妇 | 羞羞草影院 | 欧美亚洲国产日韩 | 天天射寡妇 | 激情综合av | 狠狠干影视 | 高清av网址 | 魔性诱惑 | 91av视频免费观看 | 本道久久| 欧美专区在线视频 | 成人免费91 | 午夜毛片在线 | 色a在线| 久久精品国产露脸对白 | 色伊人av | 欧美性aaa| 亚洲色图吧 | 日本乱子伦 | 国产午夜手机精彩视频 | 九色在线观看视频 | 日韩短视频 | 天天做天天躁天天躁 | 最近日韩中文字幕中文 | 国产精品亚洲一区二区 | 可以直接看的无码av | 精品一区在线看 | 中文字幕www| 亚欧成人在线 | 欧美日韩亚洲系列 | 成人va在线观看 | 中国a一片一级一片 | 一区精品二区国产 | 天天躁夜夜操 | 久色网站 | 午夜黄色网址 | 日韩亚洲欧美精品 | 韩国主播青草55部完整 | 91丝袜| 欧美三日本三级少妇99 | 国产日本欧美一区二区 | 一区二区三区四区不卡 | 丝袜美腿av | 高h免费视频 | 蜜臀aⅴ免费一区二区 | 羽月希奶水一区二区三区 | 国产精品久久久久久久专区 | 美女网站免费黄 | 国产videos| 成人免费毛片aaaaaa片 | 精品无人区无码乱码毛片国产 | 美女福利视频 | 在线免费视频观看 | 影音先锋久久久久av综合网成人 | 天天在线免费视频 | 青草精品视频 | 秋霞二区 | 日批在线| 激情丁香婷婷 | 二区在线播放 | 伊人成人在线观看 | 伊人365 | 伊人狼人综合 | 久爱视频在线观看 | 国产精品边吃奶边做爽 | 波多野结衣绝顶大高潮 | 久久久久免费精品视频 |