lock 關鍵字將語句塊標記為臨界區,方法是獲取給定對象的互斥鎖,執行語句,然後釋放該鎖。 lock語句根本使用的就是Monitor.Enter和Monitor.Exit,也就是說lock(this)時執行Monitor.Enter(this),大括弧結束時執行Monitor.Exit(this). ...
lock 關鍵字將語句塊標記為臨界區,方法是獲取給定對象的互斥鎖,執行語句,然後釋放該鎖。
lock語句根本使用的就是Monitor.Enter和Monitor.Exit,也就是說lock(this)時執行Monitor.Enter(this),大括弧結束時執行Monitor.Exit(this).他的意義在於什麼呢,對於任何一個對象來說,他在記憶體中的第一部分放置的是所有方法的地址,第二部分放著一個索引,他指向CLR中的SyncBlock Cache區域中的一個SyncBlock.什麼意思呢?就是說,當你執行Monitor.Enter(Object)時,如果object的索引值為負數,就從SyncBlock Cache中選區一個SyncBlock,將其地址放在object的索引中。這樣就完成了以object為標誌的鎖定,其他的線程想再次進行Monitor.Enter(object)操作,將獲得object為正數的索引,然後就等待。直到索引變為負數,即線程使用Monitor.Exit(object)將索引變為負數。
使用lock需要註意的地方:
1.lock不能鎖定空值某一對象可以指向Null,但Null是不需要被釋放的。(請參考:認識全面的null)
2.lock不能鎖定string類型,雖然它也是引用類型的。因為字元串類型被CLR“暫留”
這意味著整個程式中任何給定字元串都只有一個實例,就是這同一個對象表示了所有運行的應用程式域的所有線程中的該文本。因此,只要在應用程式進程中的任何位置處具有相同內容的字元串上放置了鎖,就將鎖定應用程式中該字元串的所有實例。因此,最好鎖定不會被暫留的私有或受保護成員。
3.lock鎖定的對象是一個程式塊的記憶體邊界
4.值類型不能被lock,因為前文標紅字的“對象被釋放”,值類型不是引用類型的
5.lock就避免鎖定public 類型或不受程式控制的對象。
例如,如果該實例可以被公開訪問,則 lock(this) 可能會有問題,因為不受控制的代碼也可能會鎖定該對象。這可能導致死鎖,即兩個或更多個線程等待釋放同一對象。出於同樣的原因,鎖定公共數據類型(相比於對象)也可能導致問題。
使用lock(this)的時候,類的成員變數的值可能會被不在臨界區的方法改值了
應用場景:經常會應用於防止多線程操作導致公用變數值出現不確定的異常,用於確保操作的安全性
示例
// statements_lock2.cs using System; using System.Threading; class Account { private Object thisLock = new Object(); int balance; Random r = new Random(); public Account(int initial) { balance = initial; } int Withdraw(int amount) { // This condition will never be true unless the lock statement // is commented out: if (balance < 0) { throw new Exception("Negative Balance"); } // Comment out the next line to see the effect of leaving out // the lock keyword: lock(thisLock) { if (balance >= amount) { Console.WriteLine("Balance before Withdrawal : " + balance); Console.WriteLine("Amount to Withdraw : -" + amount); balance = balance - amount; Console.WriteLine("Balance after Withdrawal : " + balance); return amount; } else { return 0; // transaction rejected } } } public void DoTransactions() { for (int i = 0; i < 100; i++) { Withdraw(r.Next(1, 100)); } } } class Test { static void Main() { Thread[] threads = new Thread[10]; Account acc = new Account(1000); for (int i = 0; i < 10; i++) { Thread t = new Thread(new ThreadStart(acc.DoTransactions)); threads[i] = t; } for (int i = 0; i < 10; i++) { threads[i].Start(); } } }
以上圖片由“圖鬥羅”提供