深入浅出多线程系列之四:简单的同步 lock
1: 考慮下下面的代碼:
class?ThreadUnsafe????{
????????static?int?_val1?=?1,?_val2?=?1;
????????internal?static?void?Go()
????????{
????????????if?(_val2?!=?0)
????????????{
????????????????Console.WriteLine(_val1?/_val2);
????????????}
????????????_val2?=?0;
????????}
????}
?
這段代碼是非線程安全的,假設(shè)有兩個(gè)線程A,B,A,B都執(zhí)行到了Go方法的if判斷中,假設(shè)_val2=1.所以?xún)蓚€(gè)線程A,B都通過(guò)if判斷,
A執(zhí)行了Console.WriteLine方法,然后退出if語(yǔ)句,執(zhí)行_val2=0,此時(shí)_val2=0.
但是此時(shí)線程B才剛剛執(zhí)行到Console.WriteLine方法,而此時(shí)_val2=0.所以你有可能會(huì)得到一個(gè)divide by zero 的異常。
?
為了保證線程安全,我們可以使用Lock關(guān)鍵字,例如:
??????? static?readonly?object?_locker?=?new?object();????????static?int?_val1?=?1,?_val2?=?1;
????????internal?static?void?Go()
????????{
????????????lock?(_locker)
????????????{
????????????????if?(_val2?!=?0)
????????????????{
????????????????????Console.WriteLine(_val1?/?_val2);
????????????????}
????????????????_val2?=?0;
????????????}
????????}
此時(shí)線程A,B都只能有一個(gè)可以獲得_locker鎖,所以只能有一個(gè)線程來(lái)執(zhí)行lock塊的代碼。
C#的Lock關(guān)鍵字實(shí)際上是Monitor.Enter,和Monitor.Exit的縮寫(xiě)。例如上面的代碼和下面的等價(jià)。
??????????? Monitor.Enter(_locker);????????????try
????????????{
????????????????if?(_val2?!=?0)
????????????????{
????????????????????Console.WriteLine(_val1?/?_val2);
????????????????}
????????????????_val2?=?0;
????????????}
????????????finally?{?Monitor.Exit(_locker);?}
如果在調(diào)用Monitor.Exit之前沒(méi)有調(diào)用Monitor.Enter,則會(huì)拋出一個(gè)異常。
?
不知道大家注意到?jīng)]有,在Monitor.Enter 和 Try 方法之間可能會(huì)拋出異常。
例如在線程上調(diào)用Abort,或者是OutOfMemoryException。
為了解決這個(gè)問(wèn)題CLR 4.0提供了Monitor.Enter的重載,增加了lockTaken 字段,當(dāng)Monitor.Enter成功獲取鎖之后,lockTaken就是True,否則為False。
我們可以將上面的代碼改成下面的版本。
??????????? bool?lockTaken?=?false;????????????try
????????????{
????????????????Monitor.Enter(_locker,?ref?lockTaken);
????????????????//?Do?something..
????????????}
????????????finally{?
????????????????if(lockTaken)?{
????????????????????Monitor.Exit(_locker);
????????????????}
????????????}
Monitor也提供了TryEnter方法,并且可以傳遞一個(gè)超時(shí)時(shí)間。如果方法返回True,則代表獲取了鎖,否則為false。
?
2:選擇同步對(duì)象。
Monitor.Enter方法的參數(shù)是一個(gè)object類(lèi)型,所以任何對(duì)象都可以是同步對(duì)象,考慮下下面的代碼:
1:鎖定值類(lèi)型會(huì)將值類(lèi)型進(jìn)行裝箱,所以Monitor.Enter進(jìn)入的是一個(gè)對(duì)象,但是Monitor.Exit()退出的是另一個(gè)不同的對(duì)象。
2,3:鎖定this和type對(duì)象,會(huì)導(dǎo)致無(wú)法控制鎖的邏輯,并且它很難保證不死鎖和頻繁的阻塞,在相同進(jìn)程中鎖定type對(duì)象會(huì)穿越應(yīng)用程序域。
4:由于字符串駐留機(jī)制,所以也不要鎖定string,關(guān)于這點(diǎn),請(qǐng)大家去逛一逛老A的博客。
?
3:嵌套鎖:
同一個(gè)線程可以多次鎖定同一對(duì)象。例如
lock(locker)????lock(locker)
????????lock(locker)
????????{
????????????//?do?something
}
或者是:
Monitor.Enter(locker);?Monitor.Enter(locker);?Monitor.Enter(locker);//Do?something.
Monitor.Exit(locker);?Monitor.Exit(locker);?Monitor.Exit(locker);
?
當(dāng)一個(gè)線程使用一個(gè)鎖調(diào)用另一方法的時(shí)候,嵌套鎖就非常的有用。例如:
?????? static?readonly?object?_locker?=?new?object();????????static?void?Main()
????????{
????????????lock?(_locker)
????????????{?
????????????????AnotherMethod();
????????????}
????????}
????????static?void?AnotherMethod()
????????{
????????????lock?(_locker){?//dosomething;}
????????}
?
4:死鎖:
先看下面的代碼:
??????? static?object?locker1?=?new?object();????????static?object?locker2?=?new?object();
????????public?static?void?MainThread()
????????{
????????????new?Thread(()?=>
????????????????{
????????????????????lock?(locker1)?? //獲取鎖locker1
????????????????????{
????????????????????????Thread.Sleep(1000);
????????????????????????lock?(locker2) //嘗試獲取locker2
????????????????????????{
????????????????????????????Console.WriteLine("locker1,locker2");
????????????????????????}
????????????????????}
????????????????}).Start();
????????????lock?(locker2) //獲取鎖locker2
????????????{
????????????????Thread.Sleep(1000);
????????????????lock?(locker1) //嘗試獲取locker1
????????????????{
????????????????????Console.WriteLine("locker2,locker1");
????????????????}
????????????}
????????}
在這里
主線程先獲取locker2的鎖,然后sleep,接著嘗試獲取locker1的鎖。
副線程先獲取locker1的鎖,然后sleep,接著嘗試獲取locker2的鎖。
程序進(jìn)入了死鎖狀態(tài),兩個(gè)線程都在等待對(duì)方釋放自己等待的鎖。?
CLR作為一個(gè)獨(dú)立宿主環(huán)境,它不像SQL Server一樣,它沒(méi)有自動(dòng)檢測(cè)死鎖機(jī)制,也不會(huì)結(jié)束一個(gè)線程來(lái)破壞死鎖。死鎖的線程會(huì)導(dǎo)致部分線程無(wú)限的等待。
?
下篇文章會(huì)介紹一些其他同步構(gòu)造。
?
?
參考資料:
http://www.albahari.com/threading/
CLR Via C# 3.0
?
轉(zhuǎn)載于:https://www.cnblogs.com/LoveJenny/archive/2011/05/23/2053604.html
總結(jié)
以上是生活随笔為你收集整理的深入浅出多线程系列之四:简单的同步 lock的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 一個傳統的C2C網站的用戶充值的过程
- 下一篇: SQL 2005 中的数据约束