回顧一下上次,我們討論了lock/AutoResetEvent/ManualResetEvent以及Semaphore。這些用於線程同 步的結構叫做同步基元。同步基元從類型上可以分為鎖定/通知/聯鎖三種。lock顯然鎖定方式,而且是獨 占鎖定,也就是在鎖釋放之前不能由其它線程獲得。 Semaphore也是一種鎖定,只不過不是獨占鎖,可以 指定多少個線程訪問代碼塊。AutoResetEvent和ManualResetEvent當然就是通知方式了,前者在通行之後 自動重置,後者需要手動重置。我們還看到了即使使用同步機制不一定能確保線程按照我們規劃的去執行 ,因為從根本上來說,操作系統的線程調度我們是沒有辦法預測的,除非使用阻塞或鎖等待等方式,否則 我們很難去預測兩個無關的線程究竟哪個先得到執行(即使設置了優先級),而且在使用這些同步機制的 時候我們也要考慮到性能問題,如果多線程程序做的不好的話很可能會比單線程執行效率還低,比如我們 開啟了多個線程相互阻塞等待並沒有任何的並行運算,比如在一個多線程環境匯總我們鎖的范圍很大,導 致多線程環境變為了一個單線程環境,有關性能問題以後再討論,這次我們來看看其它的一些同步基元。
本文的例子基於上文定義的一些基本靜態對象:
static int result = 0;
static object locker = new object();
static EventWaitHandle are = new AutoResetEvent(false);
static EventWaitHandle mre = new ManualResetEvent(false);
使用lock保護共享資源不被多個線程同時修改是常見的做法,其實lock本質上基於Monitor,而使用 Monitor本身可以帶來更豐富的特性,比如可以設置超過某個等待時間段就不繼續等待:
for (int i = 0; i < 10; i++)
{
new Thread(() =>
{
if (Monitor.TryEnter(locker, 2000))
{
Thread.Sleep(1000);
Console.WriteLine(DateTime.Now.ToString("mm:ss"));
Monitor.Exit(locker);
}
}).Start();
}
在這段代碼中我們開啟10個線程嘗試申請locker獨占鎖,通過輸出結果可以看出,由於我們設置了2秒 超時,程序只輸出了三次:
在第一個線程獲取鎖之後,一秒後釋放,第二個線程獲取,一秒後又釋放,第三個線程最後獲取到, 之後的線程都超過了2秒等待時間,TryEnter返回false,線程結束。