C# lock關(guān)鍵字?jǐn)⑹?/h1>
C#語(yǔ)言有很多值得學(xué)習(xí)的地方,這里我們主要介紹C# lock關(guān)鍵字,包括介紹Monitor 類鎖定一個(gè)對(duì)象等方面。
C#提供了一個(gè)lock關(guān)鍵字,它可以把一段代碼定義為互斥段(critical section),互斥段在一個(gè)時(shí)刻內(nèi)只允許一個(gè)線程進(jìn)入執(zhí)行,而其他線程必須等待。在C#中,C# lock關(guān)鍵字定義如下:
lock(expression) statement_block
expression代表你希望跟蹤的對(duì)象,通常是對(duì)象引用。
◆如果你想保護(hù)一個(gè)類的實(shí)例,一般地,你可以使用this;
◆如果你想保護(hù)一個(gè)靜態(tài)變量(如互斥代碼段在一個(gè)靜態(tài)方法內(nèi)部),一般使用類名就可以了。
而statement_block就是互斥段的代碼,這段代碼在一個(gè)時(shí)刻內(nèi)只可能被一個(gè)線程執(zhí)行。
下面是一個(gè)使用C# lock關(guān)鍵字的典型例子,在注釋里說(shuō)明了C# lock關(guān)鍵字的用法和用途。
示例如下:
- 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小于0則拋出異常
- throw new Exception("Negative Balance");
- }
- //下面的代碼保證在當(dāng)前線程修改balance的值完成之前
- //不會(huì)有其他線程也執(zhí)行這段代碼來(lái)修改balance的值
- //因此,balance的值是不可能小于0 的
- lock (this)
- {
- Console.WriteLine("Current Thread:"+Thread.CurrentThread.Name);
- //如果沒(méi)有l(wèi)ock關(guān)鍵字的保護(hù),那么可能在執(zhí)行完if的條件判斷之后
- //另外一個(gè)線程卻執(zhí)行了balancebalance=balance-amount修改了balance的值
- //而這個(gè)修改對(duì)這個(gè)線程是不可見(jiàn)的,所以可能導(dǎo)致這時(shí)if的條件已經(jīng)不成立了
- //但是,這個(gè)線程卻繼續(xù)執(zhí)行balancebalance=balance-amount,所以導(dǎo)致balance可能小于0
- if (balance >= amount)
- {
- Thread.Sleep(5);
- balancebalance = balance - amount;
- return amount;
- }
- else
- {
- return 0; // transaction rejected
- }
- }
- }
- 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();
- }
- }
- }
Monitor 類鎖定一個(gè)對(duì)象
當(dāng)多線程公用一個(gè)對(duì)象時(shí),也會(huì)出現(xiàn)和公用代碼類似的問(wèn)題,這種問(wèn)題就不應(yīng)該使用C# lock關(guān)鍵字了,這里需要用到System.Threading中的一個(gè)類Monitor,我們可以稱之為監(jiān)視器,Monitor提供了使線程共享資源的方案。
Monitor類可以鎖定一個(gè)對(duì)象,一個(gè)線程只有得到這把鎖才可以對(duì)該對(duì)象進(jìn)行操作。對(duì)象鎖機(jī)制保證了在可能引起混亂的情況下一個(gè)時(shí)刻只有一個(gè)線程可以訪問(wèn)這個(gè)對(duì)象。 Monitor必須和一個(gè)具體的對(duì)象相關(guān)聯(lián),但是由于它是一個(gè)靜態(tài)的類,所以不能使用它來(lái)定義對(duì)象,而且它的所有方法都是靜態(tài)的,不能使用對(duì)象來(lái)引用。下面代碼說(shuō)明了使用Monitor鎖定一個(gè)對(duì)象的情形:
- ......
- Queue oQueue=new Queue();
- ......
- Monitor.Enter(oQueue);
- ......//現(xiàn)在oQueue對(duì)象只能被當(dāng)前線程操縱了
- Monitor.Exit(oQueue);//釋放鎖
如上所示,當(dāng)一個(gè)線程調(diào)用Monitor.Enter()方法鎖定一個(gè)對(duì)象時(shí),這個(gè)對(duì)象就歸它所有了,其它線程想要訪問(wèn)這個(gè)對(duì)象,只有等待它使用 Monitor.Exit()方法釋放鎖。為了保證線程最終都能釋放鎖,你可以把Monitor.Exit()方法寫在try-catch- finally結(jié)構(gòu)中的finally代碼塊里。
【編輯推薦】