1.在單線程中,我們每次只能做一件事情。
而在多線程中,其實最本質的也是一次只能做一件事情,只是CPU給定的時間片完成之後,切換到另一個線程,然後這個分配CPU的時間片完成之後,再次切換到另一個線程,如此反復,其間切換的速度很快,給人一種同時進行的錯覺而已。
但在多線程中,往往會發生兩個線程搶占資源的問題,所以我們需要防止這些資源訪問的沖突。
C#提供線程同步機制來防止資源訪問的沖突,其中主要用到lock關鍵字、Monitor類和Mutex類
2.線程同步機制
應用程序中使用多線程的一個好處是每個線程都可以異步執行。
線程同步是指並發線程高效、有序地訪問共享資源所采用的技術。
所謂同步,是指某一時刻只有一個線程可以訪問資源,只有當資源所有者主動放棄了代碼或資源的所有權時,其他線程才可以使用這些資源
3.使用lock關鍵字實現線程同步
lock關鍵字可以用來確保代碼塊完成運行,而不會被其他線程中斷,它是通過在代碼塊運行期間為給定對象獲取互斥鎖來實現。
lock語句以關鍵字lock開頭,它有一個作為參數的對象,在該參數的後面還有一個一次只能有一個線程執行的代碼塊。
語法格式:
Object thisLock = new Object();
lock(thisLock){//要運行的代碼塊}
提供給lock語句的參數只是用來唯一標識由多個線程共享的資源,所以可以使任意類實例,實際上,此參數通常標識需要進行線程同步的資源。
避免鎖定public類型或不受應用程序控制的對象實例。盡量避免鎖定公共數據類型,鎖定字符串尤其危險。所以,最好鎖定不會被暫留的私有對象或受保護的對象。
說明:lock語句使用Monitor類實現,等效於try/finally語句塊,使用lock關鍵字通常比直接使用Monitor類更可取。首先,lock更簡潔;其次,lock確保了及時受保護的代碼引發異常,也可以釋放基礎監聽器,這是通過finally關鍵字來實現,無論是否引發一場,它都執行關聯的代碼塊。
注意:如果在靜態方法中使用lock關鍵字,則不能使用this
static void Main(string[] args) { lock(new Program()) { Console.WriteLine("鎖定線程"); Console.ReadLine(); } }
4.使用Monitor驅動對象實現線程同步
Monitor類提供了同步對對象的訪問機制,通過向單個線程授予對象鎖來控制對對象的訪問。對象鎖提供限制訪問代碼塊(臨界區)的能力。當一個線程擁有對象鎖時,其他任何線程都不能獲取該鎖。
Monitor類的主要功能:
(1)根據需要與某個對象相關聯
(2)它是未綁定的,可以直接從任何上下文調用它
(3)不能創建Monitor類的實例
Monitor類的常用方法:
Enter 在指定的對象上獲取排他鎖
Exit 釋放指定對象上的排他鎖
Pulse 通知等待隊列中的線程鎖定對象狀態的更改
PulseAll 通知所有的等待線程對象狀態的更改
TryEnter 試圖獲取指定對象的排他鎖
Wait 釋放對象上的鎖並阻止當前線程,直到它重新獲取該鎖
使用Monitor類鎖定的是對象(引用類型)而不是值類型
實例代碼:
class Program { static void Main(string[] args) { Program myProgram = new Program(); //實例化類對象 myProgram.LockThread(); //調用鎖定線程方法 Console.ReadLine(); } void LockThread() { Monitor.Enter(this); //鎖定當前線程 Console.WriteLine("鎖定線程以實現線程同步"); Monitor.Exit(this); //釋放當前線程 } }
5.使用Mutex類實現線程同步
Mutex類與監視器類似,與監視器不同的是,Mutex類可以用來是跨進程的線程同步。
使用WaitHandle.WaitOne方法請求互斥體的所屬權
擁有互斥體的線程可以在對WaitOne方法的重復調用中請求相同的互斥體而不會阻止其執行,但是記住,線程必須要調用同樣多次的ReleaseMutex方法來釋放互斥體的所屬權。
Mutex類強制線程標識,因此互斥體只能由獲得它的線程釋放。
Mutex類常用的方法
Close 在派生類中被重寫時,釋放由當前WaitHandle持有的所有資源
OpenExisting 打開現有的已命名的互斥體
ReleaseMutex 釋放Mutex一次
SignalAndWait 原子操作的形式,向一個WaitHandle發出信號並等待另一個
WaitAll 等待指定數組中的所有元素都收到信號
WaitAny 等待指定數組中的任一元素收到信號
WaitOne 在派生類中重寫,阻止當前線程,知道當前WaitHandle收到信號
實現Mutex類線程同步的步驟:
1.創建一個Mutex對象,構造函數中比較常用的有public Mutex(bool initallyOwned)
參數制定了創建該對象的線程是否希望立即獲取其所有權,當在一個資源得到保護的類中創建Mutex對象,常常將該參數設置為false
2.在需要單線程訪問的地方調用其等待方法,等待方法請求Mutex對象的所有權。如果該所有權被另一個線程擁有,則阻塞請求線程,並放到等待隊列中,請求線程將保持阻塞,直到Mutex對象受到其所有者線程發出的將其釋放的信號為止。
6.實例代碼
創建程序,自定義lockThread方法,利用Mutex對象的WaitOne方法阻止當前線程,然後再調用Mutex對象的ReleaseMutex方法釋放Mutex對象,即釋放當前線程。最後在Main方法中通過對象調用lockThread方法
class Program { static void Main(string[] args) { Program myProgram = new Program(); //實例化類對象 myProgram.LockThread(); //調用鎖定線程方法 Console.ReadLine(); } void lockThread() { Mutex myMutex = new Mutex(false); //創建Mutex對象 myMutex.WaitOne(); //阻止當前線程 Console.WriteLine("鎖定線程以實現線程同步"); myMutex.ReleaseMutex(); //釋放Mutex對象 } }
總的來說,線程同步的東西還是要多讀幾遍,才能明白。而且要不斷地實踐鍛煉才能知道lock,Monitor類和Mutex類是怎麼用的,各自的區別優點以及缺點在哪裡。
感覺學無止境啊,繼續Fighting吧