程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 改善C#程序的建議6:在線程同步中使用信號量

改善C#程序的建議6:在線程同步中使用信號量

編輯:C#入門知識

所謂線程同步,就是多個線程之間在某個對象上執行等待(也可理解為鎖定該對象),直到該對象被解除鎖定。C#中對象的類型分為引用類型和值類型。CLR在這兩種類型上的等待是不一樣的。我們可以簡單的理解為在CLR中,值類型是不能被鎖定的,也即:不能在一個值類型對象上執行等待。而在引用類型上的等待機制,則分為兩類:鎖定和信號同步。

鎖定,使用關鍵字lock和類型Monitor。兩者沒有實質區別,前者其實是後者的語法糖。這是最常用的同步技術;

本建議我們討論的是信號同步。信號同步機制中涉及的類型都繼承自抽象類WaitHandle,這些類型有EventWaitHandle(類型化為AutoResetEvent、ManualResetEvent)和Semaphore以及Mutex。見類圖6-3:

clip_image002

圖 同步功能類類圖

EventWaitHandle(子類為AutoResetEvent、ManualResetEvent)和Semaphore以及Mutex都繼承自WaitHandle,所以它們底層的原理是一致的,維護的都是一個系統內核句柄。不過我們仍需簡單的區分下這三類類型。

EventWaitHandle,維護一個由內核產生的布爾類型對象(我們稱之為“阻滯狀態”),如果其值為false,那麼在它上面等待的線程就阻塞。可以調用類型的Set方法將其值設置為true,解除阻塞。EventWaitHandle類型的兩個子類AutoResetEvent和ManualResetEvent,它們的區別並不大,本建議接下來會針對它們闡述如何正確使用信號量。

Semaphore,維護一個由內核產生的整型變量,如果其值為0,則在它上面等待的線程就阻塞,其值大於0,就解除阻塞,同時,每解除阻塞一個線程,其值就減1。

EventWaitHandle和Semaphore提供的都是單應用程序域內的線程同步功能,Mutex則不同,它為我們提供了跨應用程序域阻塞和解除阻塞線程的能力。


1:使用信號機制提供線程同步的一個簡單的例子

使用信號機制提供線程同步的一個簡單的例子如下:

        AutoResetEvent autoResetEvent = new AutoResetEvent(false);

private void buttonStartAThread_Click(object sender, EventArgs e)
{
Thread tWork = new Thread(() =>
{
label1.Text = "線程啟動..." + Environment.NewLine;
label1.Text += "開始處理一些實際的工作" + Environment.NewLine;
//省略工作代碼
label1.Text += "我開始等待別的線程給我信號,才願意繼續下去" + Environment.NewLine;
autoResetEvent.WaitOne();
label1.Text += "我繼續做一些工作,然後結束了!";
//省略工作代碼
});
tWork.IsBackground = true;
tWork.Start();
}

private void buttonSet_Click(object sender, EventArgs e)
{
//給在autoResetEvent上等待的線程一個信號
autoResetEvent.Set();
}

這是一個簡單的Winform窗體程序,其中一個按鈕負責開啟一個新的線程,還有一個按鈕負責給剛開啟的那個線程發送信號。現在詳細解釋這裡面發生的事情。

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

這段代碼創建了一個同步類型對象autoResetEvent,它設置自己的默認阻滯狀態是false。這意味著任何在它上面進行等待的線程將會被阻滯。所謂進行等待,就是在線程中應用:

autoResetEvent.WaitOne();

這說明tWork開始在autoResetEvent上等待任何其它地方給它的信號。信號來了,則tWork開始繼續工作,否則就一直等著(即阻滯)。接下來我們看到在主線程中(本例中即UI線程,它相對線程tWork來說,就是一個“另外的線程”):

autoResetEvent.Set();

主線程通過上面這句代碼負責向在autoResetEvent上等待的線程tWork上下文發送信號,即將tWork的阻滯狀態設置為true。tWork接收到這個信號,開始繼續工作。

這個例子相當簡單,但是已經完整說明了信號機制的工作原理。


2:AutoResetEvent和ManualResetEvent的區別

AutoResetEvent和ManualResetEvent有這樣的區別:前者在發送信號完畢後(即調用Set方法),自動將自己的阻滯狀態設置為false,而後者需要進行手動設定。可以通過一個例子來說明這種區別:

        AutoResetEvent autoResetEvent = new AutoResetEvent(false);

private void buttonStartAThread_Click(object sender, EventArgs e)
{
StartThread1();
StartThread2();
}

private
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved