C#中的lock、Monitor、Mutex學(xué)習(xí)筆記
線程:線程是進(jìn)程的獨(dú)立執(zhí)行單元,每一個(gè)進(jìn)程都有一個(gè)主線程,除了主線程可以包含其他的線程。
多線程的意義:多線程有助于改善程序的總體響應(yīng)性,提高CPU的效率。
多線程的應(yīng)用程序域是相當(dāng)不穩(wěn)定的,因?yàn)槎鄠€(gè)線程在同一時(shí)間內(nèi)都能運(yùn)行共享的功能模塊。為了保護(hù)應(yīng)用程序的資源不被破壞,為多線程程序提供了三種加鎖的機(jī)制,分別是:Monitor類、Lock關(guān)鍵字和Mutex類。
1. lock
lock實(shí)現(xiàn)的功能是:使后進(jìn)入的線程不會中斷當(dāng)前的線程,而是等待當(dāng)前線程結(jié)束后再繼續(xù)執(zhí)行。
應(yīng)用:
private Object thisLock=new object();
lock(thisLock){
//鎖定的代碼塊
}
注意:避免鎖定 public 類型,否則實(shí)例將超出代碼的控制范圍。
常見的結(jié)構(gòu) lock (this)、lock (typeof (MyType)) 和 lock ("myLock")
違反此準(zhǔn)則:如果實(shí)例可以被公共訪問,將出現(xiàn) lock (this) 問題。
如果 MyType 可以被公共訪問,將出現(xiàn) lock (typeof (MyType)) 問題。
由于進(jìn)程中使用同一字符串的任何其他代碼將共享同一個(gè)鎖,所以出現(xiàn) lock(“myLock”) 問題。
最佳做法是定義 private 對象來鎖定, 或 private static 對象變量來保護(hù)所有實(shí)例所共有的數(shù)據(jù)。
下面舉例說明lock的應(yīng)用:
下面的例子中創(chuàng)建了5個(gè)次線程,次線程完成的任務(wù)就是:輸出線程編碼,延遲1秒,然后輸出當(dāng)時(shí)的時(shí)間
Example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication5 {
class Program {
static void Main(string[] args) {
Console.WriteLine ("程序開始時(shí)間:"+DateTime.Now .ToString());
Example ex=new Example ();
Thread []threads=new Thread[5];
for (int i=0;i<5;i++)
{
threads[i]=new Thread (new ThreadStart(ex.OutPut));
threads[i].Name =string.Format ("Worker thread#{0}",i) ;
}
foreach(Thread t in threads){
t.Start();
}
Console.WriteLine("主線程最后一句代碼!"+DateTime.Now.ToString());
}
}
class Example{
private static object thisLock=new object ();
public void OutPut()
{
lock(thisLock){
Console .WriteLine("->{0}",Thread.CurrentThread.Name);
Thread.Sleep(1000);
Console.WriteLine(DateTime.Now);
}
}
}
}
實(shí)驗(yàn)結(jié)果如下:
圖1:加lock
圖2:沒有l(wèi)ock
從實(shí)驗(yàn)結(jié)果可以看出,加lock后,程序每次只能執(zhí)行一個(gè)線程,只有當(dāng)前線程執(zhí)行完了,才會執(zhí)行下一個(gè)線程未加lock,程序執(zhí)行混亂,容易阻塞。
2. Monitor
lock是對Monitor的Enter和Exit的一個(gè)封裝,因此Monitor類的Enter()和Exit()方法的組合使用可以用lock關(guān)鍵字替代。
Monitor類除了具有l(wèi)ock的功能外,還有以下功能:
TryEnter()解決長期死等的問題,如果一個(gè)并發(fā)經(jīng)常發(fā)生,并且持續(xù)時(shí)間很長,使用TryEnter,可以有效防止死鎖或者長時(shí)間 的等待。
Wait()釋放對象上的鎖,以便允許其他線程鎖定和訪問該對象。在其他線程訪問對象時(shí),調(diào)用線程將等待。脈沖信號用于通知等待線程有關(guān)對象狀態(tài)的更改。
Pulse(),PulseAll()向一個(gè)或多個(gè)等待線程發(fā)送信號。該信號通知等待線程鎖定對象的狀態(tài)已更改,并且鎖的所有者準(zhǔn)備釋放該鎖。等待線程被放置在對象的就緒隊(duì)列中以便它可以最后接收對象鎖。一旦線程擁有了鎖,它就可以檢查對象的新狀態(tài)以查看是否達(dá)到所需狀態(tài)。
注意:Pulse、PulseAll和Wait方法必須從同步的代碼塊內(nèi)調(diào)用。
3. Mutex(互斥體)
Mutex的突出特點(diǎn)是可以跨應(yīng)用程序域邊界對資源進(jìn)行獨(dú)占訪問,即可以用于同步不同進(jìn)程中的線程,這種功能是以犧牲更多的系統(tǒng)資源為代價(jià)的。
互斥體Mutex和事件對象EventWaitHandler屬于內(nèi)核對象,利用內(nèi)核對象進(jìn)行線程同步,線程必須要在用戶模式和內(nèi)核模式間切換,所以一般效率很低,但利用互斥對象和事件對象這樣的內(nèi)核對象,可以在多個(gè)進(jìn)程中的各個(gè)線程間進(jìn)行同步。
互斥體Mutex類似于一個(gè)接力棒,拿到接力棒的線程才可以開始跑,當(dāng)然接力棒一次只屬于一個(gè)線程(Thread Affinity),如果這個(gè)線程不釋放接力棒(Mutex.ReleaseMutex),那么其他所有需要接力棒運(yùn)行的線程只能等著看熱鬧。
相關(guān)文章
C#創(chuàng)建、部署、調(diào)用WebService圖文實(shí)例詳解
本文主要用詳細(xì)的圖文給大家介紹C#創(chuàng)建、部署、調(diào)用WebService的全部過程以及中間需要避免的問題。2017-11-11DataGridView凍結(jié)列或行、列順序調(diào)整、操作行頭列頭標(biāo)題的方法
這篇文章介紹了DataGridView凍結(jié)列或行、列順序調(diào)整、操作行頭列頭標(biāo)題的方法,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-02-02C#編程自學(xué)之?dāng)?shù)據(jù)類型和變量三
C#語言類型系統(tǒng)提出的一個(gè)核心概念裝箱(boxing)拆箱(unboxing)。裝箱和取消裝箱的概念是C#的類型系統(tǒng)的核心。它在“值類型”和“引用類型”之間的架起了一座橋梁,使得任何“值類型”的值都可以轉(zhuǎn)換為object類型的值,反過來轉(zhuǎn)換也可以。2015-10-10C# CAD SelectionFilter下TypedValue數(shù)組使用方式
這篇文章主要介紹了C# CAD SelectionFilter下TypedValue數(shù)組使用方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02C#編程報(bào)錯(cuò)System.InvalidOperationException問題及解決
這篇文章主要介紹了C#編程報(bào)錯(cuò)System.InvalidOperationException問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05DataTable的AcceptChanges()和RejectChanges()方法介紹并實(shí)現(xiàn)DataGridView
這篇文章介紹了DataTable的AcceptChanges()和RejectChanges()方法并實(shí)現(xiàn)DataGridView數(shù)據(jù)增、刪、改,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02DevExpress之SplashScreen用法實(shí)例
這篇文章主要介紹了DevExpress中SplashScreen的用法,對于C#初學(xué)者有很好的參考借鑒價(jià)值,需要的朋友可以參考下2014-08-08