淺談C#中的同步鎖
本文介紹C# lock關鍵字,C#提供了一個關鍵字lock,它可以把一段程式碼定義為互斥段(critical section),互斥段在一個時刻內只允許一個執行緒進入執行,而其他執行緒必須等待。
每個執行緒都有自己的資源,但是程式碼區是共享的,即每個執行緒都可以執行相同的函式。這可能帶來的問題就是幾個執行緒同時執行一個函式,導致資料的混亂,產生不可預料的結果,因此我們必須避免這種情況的發生。
其中,lock是一種比較好用的簡單的執行緒同步方式,它是通過為給定物件獲取互斥鎖來實現同步的。它可以保證當一個執行緒在關鍵程式碼段的時候,另一個執行緒不會進來,它只能等待,等到那個執行緒物件被釋放,也就是說執行緒出了臨界區。用法:
- public void Function()
- {
- object lockThis = new object ();
- lock (lockThis)
- {
- // Access thread-sensitive resources.
- }
- }
下面是一個比較典型的使用C# lock關鍵字的例子,其中在註釋裡說明了C# lock關鍵字的用法和用途。
- Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->using System;
- using System.Threading;
- namespace ThreadSimple
- {
- internal class Account
- {
- int balance; //餘額
- Random r=new Random();
- internal Account(int initial)
- {
- balance=initial;
- }
- internal int Withdraw(int amount) //取回、取款
- {
- if(balance<0)
- {
- //如果balance
- throw new Exception("NegativeBalance");//負的 餘額
- }
- //下面的程式碼保證在當前執行緒修改balance的值完成之前
- //不會有其他執行緒也執行這段程式碼來修改balance的值
- //因此,balance的值是不可能小於0的
- lock(this)
- {
- Console.WriteLine("CurrentThread:"+Thread.CurrentThread.Name);
- //如果沒有lock關鍵字的保護,那麼可能在執行完if的條件判斷(成立)之後
- //另外一個執行緒卻執行了balance=balance-amount修改了balance的值
- //而這個修改對這個執行緒是不可見的,所以可能導致這時if的條件已經不成立了
- //但是,這個執行緒卻繼續執行 balance=balance-amount,所以導致balance可能小於0
- if(balance>=amount)
- {
- Thread.Sleep(5);
- balance=balance-amount;
- return amount;
- } else
- {
- return 0;
- //transactionrejected
- }
- }
- }
- internal void DoTransactions()//取款事務
- {
- for (int i = 0; i < 100; i++)
- {
- Withdraw(r.Next(-50, 100));
- }
- }
- }
- internal class Test
- {
- static internal Thread[] threads=new Thread[10];
- public static void Main()
- {
- Account acc=new Account(0);
- 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].Name = i.ToString();
- }
- for (int i = 0; i < 10; i++)
- {
- threads[i].Start();
- Console.ReadLine();
- }
- }
- }
- }
lock的引數必須是基於引用型別的物件,不要是基本型別像bool,int什麼的,這樣根本不能同步,原因是lock的引數要求是物件,如果傳入int,勢必要發生裝箱操作,這樣每次lock的都將是一個新的不同的物件。最好避免使用public型別或不受程式控制的物件例項,因為這樣很可能導致死鎖。特別是不要使用字串作為lock的引數,因為字串被CLR“暫留”,就是說整個應用程式中給定的字串都只有一個例項,因此更容易造成死鎖現象。建議使用不被“暫留”的私有或受保護成員作為引數。其實某些類已經提供了專門用於被鎖的成員,比如Array型別提供SyncRoot,許多其它集合型別也都提供了SyncRoot。
所以,使用lock應該注意以下幾點:
1、如果一個類的例項是public的,最好不要lock(this)。因為使用你的類的人也許不知道你用了lock,如果他new了一個例項,並且對這個例項上鎖,就很容易造成死鎖。
2、如果MyType是public的,不要lock(typeof(MyType))。
3、永遠也不要lock一個字串