深入多線程之:雙向信號(hào)與競(jìng)賽的用法分析
雙向信號(hào)和競(jìng)賽(Two-Way Signaling and Races)
Monitor.Pulse方法的一個(gè)重要特性是它是異步執(zhí)行的,這意味著調(diào)用pulse方法并不會(huì)阻塞自己等待Monitor.Pulse返回。如果任何一個(gè)線程在pulsed 對(duì)象上等待,它是不會(huì)阻塞的,換句話說(shuō),調(diào)用Monitor.Pulse對(duì)程序不會(huì)有什么作用,你可以認(rèn)為Monitor.Pulse方法被忽略了。
這樣Pulse提供了一個(gè)單向通信:一個(gè) pulsing線程悄悄的向一個(gè)waiting 線程發(fā)送信號(hào)。
Pulse并不會(huì)返回一個(gè)值來(lái)告訴你waiting線程是否收到信號(hào)。
但是有時(shí)候我們需要知道waiting線程是否受到信號(hào),例如下面的例子:
class Race
{
static readonly object _locker = new object();
static bool _go;
public static void MainThread()
{
new Thread(SaySomething).Start();
for (int i = 0; i < 5; i++)
{
lock (_locker)
{
_go = true;
Monitor.PulseAll(_locker); //通知等待的隊(duì)列
}
}
}
static void SaySomething()
{
for (int i = 0; i < 5; i++)
{
lock (_locker)
{
while (!_go) Monitor.Wait(_locker); //如果_go 為false,那么開(kāi)始阻塞。
_go = false;
Console.WriteLine("Wassup?");
}
}
}
}
期待的輸出:
Wassup?
Wassup?
Wassup?
Wassup?
Wassup?
實(shí)際的輸出:
Wassup? (線程等待)
在SaySomething方法中,for循環(huán)執(zhí)行到while,此時(shí)_go為false,所以Monitor.Wait開(kāi)始等待。在MainThread中,for循環(huán)設(shè)置_go為true。然后PulseAll.但是PulseAll方法是異步的。
所以在SaySomething線程被喚醒前,mainThread中的for循環(huán)可能已經(jīng)執(zhí)行完畢。所以SaySomething方法中的第一個(gè)Wait線程收到消息詞是_go為true,所以往下執(zhí)行,再次將_go字段設(shè)置為false。輸出”Wassup?”,但是下次循環(huán)由于_go為false,所以需要再次wait.所以實(shí)際的輸出打印了一個(gè)Wassup,然后開(kāi)始等待。
我們需要主線程在每一次迭代中如果worker仍然在執(zhí)行上一個(gè)任務(wù),那么主線程阻塞。等到worker執(zhí)行完畢,那么主線程恢復(fù)執(zhí)行,然后執(zhí)行迭代。
我們可以增加一個(gè)_ready 標(biāo)志,從而控制主線程在設(shè)置_go 標(biāo)志之前worker線程已經(jīng)ready了。也就是說(shuō)主線程在設(shè)置_go之前,會(huì)等待worker完成任務(wù),然后等待worker將ready設(shè)為true,當(dāng)worker將ready設(shè)置為true后,通過(guò)pulse來(lái)通知主線程。
class Race
{
static readonly object _locker = new object();
static bool _ready, _go;
public static void MainThread()
{
new Thread(SaySomething).Start();
for (int i = 0; i < 5; i++)
{
lock (_locker)
{
while (!_ready) Monitor.Wait(_locker); //如果worker的ready為false,則等待worker。
_ready = false; //重置標(biāo)志
_go = true;
Monitor.PulseAll(_locker);
}
}
}
static void SaySomething()
{
for (int i = 0; i < 5; i++)
{
lock (_locker)
{
_ready = true; //將ready設(shè)置為true
Monitor.PulseAll(_locker); //通知主線程,worker已經(jīng)ready了,可以執(zhí)行任務(wù)了。
while (!_go) Monitor.Wait(_locker);
_go = false;
Console.WriteLine("Wassup?");
}
}
}
}
相關(guān)文章
C#實(shí)現(xiàn)圖像選擇驗(yàn)證碼的示例代碼
為了防止網(wǎng)站被非法登陸,網(wǎng)站一般通過(guò)驗(yàn)證碼的方式,防止黑客用軟件非法登陸,本文主要介紹了C#實(shí)現(xiàn)圖像選擇驗(yàn)證碼的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08winform實(shí)現(xiàn)關(guān)閉按鈕失效的兩種方法
這篇文章主要介紹了winform實(shí)現(xiàn)關(guān)閉按鈕失效的兩種方法,實(shí)例分析了WinForm實(shí)現(xiàn)關(guān)閉按鈕失效的原理與所涉及的相關(guān)技巧,需要的朋友可以參考下2015-09-09C#實(shí)現(xiàn)多選項(xiàng)卡的瀏覽器控件
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)多選項(xiàng)卡的瀏覽器控件的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-03-03C#中BitConverter.ToUInt16()和BitConverter.ToString()的簡(jiǎn)單使用
這篇文章主要介紹了C#中BitConverter.ToUInt16()和BitConverter.ToString()的簡(jiǎn)單使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02通過(guò)C#編寫(xiě)一個(gè)簡(jiǎn)易的Windows截屏增強(qiáng)工具
在使用?Windows?系統(tǒng)的截屏快捷鍵?PrintScreen?截屏?xí)r,如果需要把截屏保存到文件,需要先粘貼到畫(huà)圖工具然后另存為文件。所以本文用C#編寫(xiě)了一個(gè)簡(jiǎn)易的Windows截屏增強(qiáng)工具,需要的可以參考一下2022-05-05