Lock VS Monitor
介紹
介紹
對(duì)開(kāi)發(fā)人員來(lái)說(shuō),處理關(guān)鍵代碼部分的多線程應(yīng)用程序是非常重要的。
Monitor和lock是c#語(yǔ)言中多線程應(yīng)用程序中提供線程安全的方法(lock關(guān)鍵字的本質(zhì)就是對(duì)Monitor的封裝)。兩者都提供了一種機(jī)制來(lái)確保只有一個(gè)線程同時(shí)執(zhí)行代碼,以避免代碼功能被其他線程中斷
鎖
c#中 Lock關(guān)鍵字確保一個(gè)線程同時(shí)執(zhí)行一段代碼。lock關(guān)鍵字確保一個(gè)線程不進(jìn)入代碼的鎖定區(qū),而另一個(gè)線程在鎖定區(qū)內(nèi)。
Lock關(guān)鍵字是Monitor的“快捷方式”。
namespace Monitor_Lock { class Program { static readonly object _object = new object(); static void TestLock() { lock (_object) { Thread.Sleep(100); Console.WriteLine(Environment.TickCount); } } static void Main(string[] args) { for (int i = 0; i < 10; i++) { ThreadStart start = new ThreadStart(TestLock); new Thread(start).Start(); } Console.ReadLine(); } } }輸出:
這里我們看到一個(gè)靜態(tài)方法“TestLock”,它在對(duì)象上使用lock語(yǔ)句。在新線程上多次調(diào)用TestLock方法時(shí),每次調(diào)用該方法都會(huì)訪問(wèn)該鎖的對(duì)象是否釋放。
Main方法創(chuàng)建十個(gè)新線程,然后在每個(gè)線程上開(kāi)始調(diào)用。方法TestLock被調(diào)用十次,但是Environment.TickCount計(jì)數(shù)器顯示受保護(hù)的方法區(qū)域是按順序執(zhí)行的,大約相隔100毫秒。
如果另一個(gè)線程試圖進(jìn)入一個(gè)鎖定的代碼,它將等待,阻塞,直到對(duì)象被釋放。
lock關(guān)鍵字通過(guò)獲取給定對(duì)象的互斥鎖,將語(yǔ)句塊標(biāo)記為一個(gè)臨界段,執(zhí)行語(yǔ)句,然后釋放鎖,
Monitor
Monitor提供了一種同步對(duì)象訪問(wèn)的機(jī)制。它可以通過(guò)獲取一個(gè)重要的鎖來(lái)實(shí)現(xiàn),這樣一次只有一個(gè)線程可以進(jìn)入給定的代碼段。Monitor與lock沒(méi)有什么不同,但是Monitor類(lèi)對(duì)試圖訪問(wèn)相同代碼鎖的各個(gè)線程的同步提供了更多的控制。
使用Monitor可以確保不允許任何其他線程訪問(wèn)鎖所有者正在執(zhí)行的應(yīng)用程序代碼段,除非其他線程使用不同的鎖定對(duì)象執(zhí)行代碼。
Monitor類(lèi)有以下方法通過(guò)獲取和釋放鎖來(lái)同步訪問(wèn)代碼的某個(gè)區(qū)域
| Enter(Object) | 在指定對(duì)象上獲取排他鎖。 |
| Enter(Object, Boolean) | 獲取指定對(duì)象上的排他鎖,并自動(dòng)設(shè)置一個(gè)值,指示是否獲取了該鎖。 |
| Exit(Object) | 釋放指定對(duì)象上的排他鎖。 |
| IsEntered(Object) | 確定當(dāng)前線程是否保留指定對(duì)象鎖。 |
| Pulse(Object) | 通知等待隊(duì)列中的線程鎖定對(duì)象狀態(tài)的更改。 |
| PulseAll(Object) | 通知所有的等待線程對(duì)象狀態(tài)的更改。 |
| TryEnter(Object, TimeSpan, Boolean) | 在指定的一段時(shí)間內(nèi)嘗試獲取指定對(duì)象上的排他鎖,并自動(dòng)設(shè)置一個(gè)值,指示是否獲得了該鎖。 |
| TryEnter(Object, Int32, Boolean) | 在指定的毫秒數(shù)內(nèi)嘗試獲取指定對(duì)象上的排他鎖,并自動(dòng)設(shè)置一個(gè)值,指示是否獲取了該鎖。 |
| TryEnter(Object, TimeSpan) | 在指定的時(shí)間內(nèi)嘗試獲取指定對(duì)象上的排他鎖。 |
| TryEnter(Object, Boolean) | 嘗試獲取指定對(duì)象上的排他鎖,并自動(dòng)設(shè)置一個(gè)值,指示是否獲取了該鎖。 |
| TryEnter(Object) | 嘗試獲取指定對(duì)象的排他鎖。 |
| TryEnter(Object, Int32) | 在指定的毫秒數(shù)內(nèi)嘗試獲取指定對(duì)象上的排他鎖。 |
| Wait(Object, Int32, Boolean) | 釋放對(duì)象上的鎖并阻止當(dāng)前線程,直到它重新獲取該鎖。?如果已用指定的超時(shí)時(shí)間間隔,則線程進(jìn)入就緒隊(duì)列。?此方法還指定是否在等待之前退出上下文的同步域(如果處于同步上下文中的話)然后重新獲取該同步域。 |
| Wait(Object) | 釋放對(duì)象上的鎖并阻止當(dāng)前線程,直到它重新獲取該鎖。 |
| Wait(Object, Int32) | 釋放對(duì)象上的鎖并阻止當(dāng)前線程,直到它重新獲取該鎖。?如果已用指定的超時(shí)時(shí)間間隔,則線程進(jìn)入就緒隊(duì)列。 |
| Wait(Object, TimeSpan) | 釋放對(duì)象上的鎖并阻止當(dāng)前線程,直到它重新獲取該鎖。?如果已用指定的超時(shí)時(shí)間間隔,則線程進(jìn)入就緒隊(duì)列。 |
| Wait(Object, TimeSpan, Boolean) | 釋放對(duì)象上的鎖并阻止當(dāng)前線程,直到它重新獲取該鎖。?如果已用指定的超時(shí)時(shí)間間隔,則線程進(jìn)入就緒隊(duì)列。?可以在等待之前退出同步上下文的同步域,隨后重新獲取該域。 |
Monitor鎖定對(duì)象(即引用類(lèi)型),而不是值類(lèi)型。雖然您可以傳遞一個(gè)值類(lèi)型來(lái)進(jìn)入和退出,但是對(duì)于每個(gè)調(diào)用,它都是單獨(dú)裝箱的。
Wait在鎖被持有并等待被通知時(shí)釋放鎖。當(dāng)Wait被通知時(shí),它返回并再次獲得鎖。Pulse和PulseAll都為等待隊(duì)列中的下一個(gè)線程的開(kāi)始發(fā)出信號(hào)。
下面是使用Monitor的語(yǔ)法。
try { int x = 1; Monitor.Enter(x); try { // Code that needs to be protected by the monitor. } finally { Monitor.Exit(x); } } catch (SynchronizationLockException SyncEx) { Console.WriteLine("A SynchronizationLockException occurred. Message:"); Console.WriteLine(SyncEx.Message); }簡(jiǎn)單例子:
輸出:
在c# 4.0中,Monitor.Enter(_object,ref _lockTaken)重載函數(shù)獲取一個(gè)獨(dú)占鎖和指定的對(duì)象,并自動(dòng)設(shè)置一個(gè)值,該值指示鎖是否被獲取。
與lock等價(jià)的Monitor實(shí)現(xiàn)
Monitor.Enter(object); try { // Your code here... } finally { Monitor.Exit(object); }結(jié)論
Monitor類(lèi)是一個(gè)靜態(tài)類(lèi),不能創(chuàng)建它的實(shí)例。
Monitor類(lèi)對(duì)象使用 Monitor.TryEnter, and Monitor.Exit 方法。一旦鎖定了代碼區(qū)域,就可以使用 Monitor.Wait, Monitor.Pulse, and Monitor.PulseAll 等方法。
Lock和monitor在多線程中基本上用于相同的目的,Monitor的不同之處在于,當(dāng)我們希望對(duì)運(yùn)行特定代碼段的多個(gè)線程的同步進(jìn)行更多控制時(shí)更有效
總結(jié)
以上是生活随笔為你收集整理的Lock VS Monitor的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 荐读|属性与可直接访问的数据成员之间应该
- 下一篇: 程序员修神之路--做好分库分表其实很难之