<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
lock 用於讀一個參照型別進行加鎖,同一時刻內只有一個執行緒能夠存取此物件。lock 是語法糖,是通過 Monitor 來實現的。
Lock 鎖定的物件,應該是靜態的參照型別(字串除外)。
實際上字串也可以作為鎖的物件使用,只是由於字串物件的特殊性,可能會造成不同位置的不同執行緒衝突。
如果你能保證字串的唯一性,例如 Guid 生成的字串,也是可以作為鎖的物件使用的(但不建議)。
鎖的物件也不一定要靜態才行,也可以通過類範例的成員變數,作為鎖物件。
lock 是 Monitor 的語法糖,生成的程式碼對比:
lock (x) { // Your code... }
object __lockObj = x; bool __lockWasTaken = false; try { System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken); // Your code... } finally { if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj); }
這裡先不理會 Monitor,後面再說。
首先,如果像下面這樣寫的話,拉出去打 si 吧。
public void MyLock() { object o = new object(); lock (o) { // } }
下面編寫一個簡單的鎖,範例如下:
class Program { private static object obj = new object(); private static int sum = 0; static void Main(string[] args) { Thread thread1 = new Thread(Sum1); thread1.Start(); Thread thread2 = new Thread(Sum2); thread2.Start(); while (true) { Console.WriteLine($"{DateTime.Now.ToString()}:" + sum); Thread.Sleep(TimeSpan.FromSeconds(1)); } } public static void Sum1() { sum = 0; lock (obj) { for (int i = 0; i < 10; i++) { sum += i; Console.WriteLine("Sum1"); Thread.Sleep(TimeSpan.FromSeconds(2)); } } } public static void Sum2() { sum = 0; lock (obj) { for (int i = 0; i < 10; i++) { sum += 1; Console.WriteLine("Sum2"); Thread.Sleep(TimeSpan.FromSeconds(2)); } } } }
類將自己設定為鎖, 這可以防止惡意程式碼對公共物件採用做鎖。
例如:
public void Access() { lock(this) {} }
鎖可以阻止其它執行緒執行鎖塊(lock(o){})中的程式碼,當鎖定時,其它執行緒必須等待鎖中的執行緒執行完成並釋放鎖。但是這可能會給程式帶來效能影響。
鎖不太適合I/O場景,例如檔案I/O,繁雜的計算或者操作比較持久的過程,會給程式帶來很大的效能損失。
10 種優化鎖的效能方法: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/
此物件提供同步存取物件的機制;Monotor 是一個靜態型別,其方法比較少,常用方法如下:
操作 | 說明 |
---|---|
Enter, TryEnter | 獲取物件的鎖。 此操作還標記關鍵節的開頭。 其他任何執行緒都不能輸入臨界區,除非它使用不同的鎖定物件執行臨界區中的說明。 |
Wait | 釋放物件的鎖,以允許其他執行緒鎖定並存取物件。 呼叫執行緒會等待另一個執行緒存取物件。 使用脈衝訊號通知等待執行緒關於物件狀態的更改。 |
Pulse 、PulseAll | 將訊號傳送到一個或多個等待執行緒。 訊號通知等待執行緒:鎖定物件的狀態已更改,鎖的所有者已準備好釋放該鎖。 正在等待的執行緒置於物件的就緒佇列中,因此它可能最終接收物件的鎖。 執行緒鎖定後,它可以檢查物件的新狀態,以檢視是否已達到所需的狀態。 |
Exit | 釋放物件的鎖。 此操作還標記受鎖定物件保護的臨界區的結尾。 |
下面是一個很簡單的範例:
private static object obj = new object(); private static bool acquiredLock = false; public static void Test() { try { Monitor.Enter(obj, ref acquiredLock); } catch { } finally { if (acquiredLock) Monitor.Exit(obj); } }
Monitor.Enter
鎖定 obj 這個物件,並且設定 acquiredLock 為 true,告訴別人 obj 已經被鎖定。
最後結束時,判斷 acquiredLock ,釋放鎖,並設定 acquiredLock 為 false。
臨界區:指被某些符號包圍的範圍。例如 {}
內。
Monitor 物件的 Enter 和 Exit 方法來標記臨界區的開頭和結尾。
Enter()
方法獲取鎖後,能夠保證只有單個執行緒能夠使用臨界區中的程式碼。使用 Monitor 類,最好搭配 try{...}catch{...}finally{...}
來使用,因為如果獲取到鎖但是沒有釋放鎖的話,會導致其它執行緒無限阻塞,即發生死鎖。
一般來說,lock 關鍵字夠用了。
下面示範了多個執行緒如何使用 Monitor 來實現鎖:
private static object obj = new object(); private static bool acquiredLock = false; static void Main(string[] args) { new Thread(Test1).Start(); Thread.Sleep(1000); new Thread(Test2).Start(); } public static void Test1() { try { Monitor.Enter(obj, ref acquiredLock); for (int i = 0; i < 10; i++) { Console.WriteLine("Test1正在鎖定資源"); Thread.Sleep(1000); } } catch { } finally { if (acquiredLock) Monitor.Exit(obj); Console.WriteLine("Test1已經釋放資源"); } } public static void Test2() { bool isGetLock = false; Monitor.Enter(obj); try { Monitor.Enter(obj, ref acquiredLock); for (int i = 0; i < 10; i++) { Console.WriteLine("Test2正在鎖定資源"); Thread.Sleep(1000); } } catch { } finally { if (acquiredLock) Monitor.Exit(obj); Console.WriteLine("Test2已經釋放資源"); } }
如果物件已經被鎖定,另一個執行緒使用 Monitor.Enter
物件,就會一直等待另一個執行緒解除鎖定。
但是,如果一個執行緒發生問題或者出現死鎖的情況,鎖一直被鎖定呢?或者執行緒具有時效性,超過一段時間不執行,已經沒有了意義呢?
我們可以通過 Monitor.TryEnter()
來設定等待時間,超過一段時間後,如果鎖還沒有釋放,就會返回 false。
改造上面的範例如下:
private static object obj = new object(); private static bool acquiredLock = false; static void Main(string[] args) { new Thread(Test1).Start(); Thread.Sleep(1000); new Thread(Test2).Start(); } public static void Test1() { try { Monitor.Enter(obj, ref acquiredLock); for (int i = 0; i < 10; i++) { Console.WriteLine("Test1正在鎖定資源"); Thread.Sleep(1000); } } catch { } finally { if (acquiredLock) Monitor.Exit(obj); Console.WriteLine("Test1已經釋放資源"); } } public static void Test2() { bool isGetLock = false; isGetLock = Monitor.TryEnter(obj, 500); if (isGetLock == false) { Console.WriteLine("鎖還沒有釋放,我不幹活了"); return; } try { Monitor.Enter(obj, ref acquiredLock); for (int i = 0; i < 10; i++) { Console.WriteLine("Test2正在鎖定資源"); Thread.Sleep(1000); } } catch { } finally { if (acquiredLock) Monitor.Exit(obj); Console.WriteLine("Test2已經釋放資源"); } }
到此這篇關於C#多執行緒系列之多執行緒鎖lock和Monitor的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45