lock 關鍵字可將陳述式區塊標記為關鍵區段 (Critical Section),其做法是為指定的物件取得互斥鎖定、執行陳述式,接著釋出該鎖定。 下列範例包含一個 lock 陳述式。
1 class Account 2 { 3 decimal balance; 4 private Object thisLock = new Object(); 5 6 public void Withdraw(decimal amount) 7 { 8 lock (thisLock) 9 { 10 if (amount > balance) 11 { 12 throw new Exception("Insufficient funds"); 13 } 14 balance -= amount; 15 } 16 } 17 }
lock 關鍵字可保證有執行緒在關鍵區段時,絕對不會有任何其他執行緒同時也進入這個關鍵區段執行。 如果其他執行緒嘗試進入已鎖定的程式碼,它將會等候、封鎖,直到該物件被釋出。
執行緒 (C# 和 Visual Basic) 這一章節會討論執行緒的處理。
lock 關鍵字會在區塊開始執行時呼叫 Enter,並在區塊結束時呼叫 Exit。 ThreadInterruptedException 擲回,如果 Interrupt 中斷等待進入 lock 陳述式的執行緒。
一般而言,請避免鎖定 public 型別或程式碼無法控制的執行個體。 有三種常見的建構,分別為 lock (this)、lock (typeof (MyType)) 和 lock ("myLock"),違反這項方針:
-
lock (this) 在可公開存取執行個體的情況下,會是問題所在。
-
lock (typeof (MyType)) 在可公開存取 MyType 的情況下,會是問題所在。
-
lock("myLock") 會是問題所在,因為使用相同字串的處理序中若有任何其他程式碼,將會共用相同的鎖定。
最佳作法是定義要鎖定的 private 物件,或者定義 private static 物件變數保護所有執行個體通用的資料。
您在 lock 陳述式的主體中不能使用 等候 關鍵字。
//using System.Threading; class ThreadTest { public void RunMe() { Console.WriteLine("RunMe called"); } static void Main() { ThreadTest b = new ThreadTest(); Thread t = new Thread(b.RunMe); t.Start(); } } // Output: RunMe called
下列範例使用到執行緒與 lock。 只要 lock 陳述式存在,此陳述式區塊就是關鍵區段,而且 balance 永遠不會成為負數。
1 // using System.Threading; 2 3 class Account 4 { 5 private Object thisLock = new Object(); 6 int balance; 7 8 Random r = new Random(); 9 10 public Account(int initial) 11 { 12 balance = initial; 13 } 14 15 int Withdraw(int amount) 16 { 17 18 // This condition never is true unless the lock statement 19 // is commented out. 20 if (balance < 0) 21 { 22 throw new Exception("Negative Balance"); 23 } 24 25 // Comment out the next line to see the effect of leaving out 26 // the lock keyword. 27 lock (thisLock) 28 { 29 if (balance >= amount) 30 { 31 Console.WriteLine("Balance before Withdrawal : " + balance); 32 Console.WriteLine("Amount to Withdraw : -" + amount); 33 balance = balance - amount; 34 Console.WriteLine("Balance after Withdrawal : " + balance); 35 return amount; 36 } 37 else 38 { 39 return 0; // transaction rejected 40 } 41 } 42 } 43 44 public void DoTransactions() 45 { 46 for (int i = 0; i < 100; i++) 47 { 48 Withdraw(r.Next(1, 100)); 49 } 50 } 51 } 52 53 class Test 54 { 55 static void Main() 56 { 57 Thread[] threads = new Thread[10]; 58 Account acc = new Account(1000); 59 for (int i = 0; i < 10; i++) 60 { 61 Thread t = new Thread(new ThreadStart(acc.DoTransactions)); 62 threads[i] = t; 63 } 64 for (int i = 0; i < 10; i++) 65 { 66 threads[i].Start(); 67 } 68 } 69 }