C#中l(wèi)ock死鎖實例教程
在c#中有個關(guān)鍵字lock,它的作用是鎖定某一代碼塊,讓同一時間只有一個線程訪問該代碼塊,本文就來談?wù)刲ock關(guān)鍵字的原理和其中應(yīng)注意的幾個問題:
lock的使用原型是:
lock(X) { //需要鎖定的代碼.... }
首先要明白為什么上面這段話能夠鎖定代碼,其中的奧妙就是X這個對象,事實上X是任意一種引用類型,它在這兒起的作用就是任何線程執(zhí)行到lock(X)時候,X需要獨享才能運行下面的代碼,若假定現(xiàn)在有3個線程A,B,C都執(zhí)行到了lock(X)而ABC因為此時都占有X,這時ABC就要停下來排個隊,一個一個使用X,從而起到在下面的代碼塊內(nèi)只有一個線程在運行(因為此時只有一個線程獨享X,其余兩個在排隊),所以這個X必須是所有要執(zhí)行臨界區(qū)域代碼進程必須共有的一個資源,從而起到抑制線程的作用。
下面再來談?wù)刲ock使用中會遇到和注意的問題,lock最需要注意的一個問題就是線程死鎖!
在MSDN上列出了3個典型問題:
通常,應(yīng)避免鎖定 public 類型,否則實例將超出代碼的控制范圍。常見的結(jié)構(gòu) lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 違反此準(zhǔn)則:
如果實例可以被公共訪問,將出現(xiàn) lock (this) 問題。
如果 MyType 可以被公共訪問,將出現(xiàn) lock (typeof (MyType)) 問題。
由于進程中使用同一字符串的任何其他代碼將共享同一個鎖,所以出現(xiàn) lock(“myLock”) 問題。
最佳做法是定義 private 對象來鎖定, 或 private shared 對象變量來保護所有實例所共有的數(shù)據(jù)。
(1)lock (this) 問題:
假定有兩個類:
class A{} class B{}
有兩個公共對象:
A a=new A(); B b=new B();
首先在A中若有一函數(shù)內(nèi)的代碼需要鎖定:
代碼1:
lock(this)//this在這里就是a { //.... lock(b) { //...... } }
然而此時B中某函數(shù)也有如下代碼需要鎖定:
代碼2:
lock(this)//this在這里就是b { //.... lock(a) { //...... } }
設(shè)想一下上面兩段代碼在兩個線程下同時執(zhí)行會有什么后果?
結(jié)果就是,代碼1執(zhí)行到lock(this)后a被鎖定,代碼2執(zhí)行到lock(this)后b被鎖定,然后代碼1需求b,代碼2需求a,此時兩個需求都被相互占有出現(xiàn)僵持狀態(tài),程序死鎖了。
(2)lock(typeof (MyType))問題:
假定有兩個公共變量:
int a;float b;
下面看如下代碼
代碼3:
lock(typeof(a))//typeof(a)就是System.type.Int類型 { //.... lock(typeof(b)) { //...... } }
又有如下代碼:
代碼4:
lock(typeof(b))//typeof(b)就是System.type.Float類型 { //.... lock(typeof(a)) { //...... } }
若有兩個進程分別同時進入上面兩個代碼外層的lock,就分別鎖定了System.type.Int和System.type.Float,而馬上它們又需求System.type.Float和System.type.Int,彼此相互占有,彼此僵持,程序進入死鎖狀態(tài)!
(3)字符串問題 :
在闡述這個問題之前,有一個知識大家必須知道:C#中字符串被公共語言運行庫 (CLR)“暫留”。這意味著整個程序中任何給定字符串都只有一個實例,就是這同一個對象表示了所有運行的應(yīng)用程序域的所有線程中的該文本。因此,只要在應(yīng)用程序進程中的任何位置處具有相同內(nèi)容的字符串上放置了鎖,就將鎖定應(yīng)用程序中該字符串的所有實例。
言下之意就是假定有兩個類分別有兩個字符串:
class A { string a="abc"; string b="def"; } class c { string c="abc"; string d="def"; }
事實上a和c引用的是同一個字符串"abc",b和d引用的是同一個字符串"def"
現(xiàn)在如果在兩個類中有如下代碼
在類A中有代碼5:
lock(b)//b是"def" { //.... lock(a)//a是"abc" { //...... } }
在類B中有代碼6:
lock(c)//c是"abc" { //.... lock(d)//d是"def" { //...... } }
那么代碼5和代碼6同時有兩個線程執(zhí)行結(jié)果可想而知:在兩個線程執(zhí)行到外層lock代碼時"def"和"abc"被鎖定。接著他們在內(nèi)部lock處同時需求"abc"和"def",而此時兩個字符串被兩個進程彼此占有,程序又死鎖了!所以MSDN說:鎖定字符串尤其危險!最好不要使用!
MSDN最后說了:最佳做法是定義 private 對象來鎖定, 或 private shared 對象變量來保護所有實例所共有的數(shù)據(jù)。
在個人看來,也不是絕對安全,這里就舉出一個例子:
假定有一個類:
class A { private Object a=new Object(); private Object b=new Object(); public void x() { lock(a) { //..... lock(b) { //.... } } } public void y() { lock(b) { //..... lock(a) { //.... } } } }
現(xiàn)在假定有兩個線程同時執(zhí)行函數(shù)x()和y();結(jié)果private對象a和b分別在外層lock鎖定,接著兩個線程在內(nèi)部又立馬需求b和a,a,b彼此占有又彼此需求,程序死鎖。
所以具體要看情況而定,但是定義 private 對象來鎖定至少可以降低風(fēng)險。
希望本文所述C#中l(wèi)ock的應(yīng)用對大家的C#程序設(shè)計有所幫助。
相關(guān)文章
C#實現(xiàn)日期格式轉(zhuǎn)換的公共方法類實例
這篇文章主要介紹了C#實現(xiàn)日期格式轉(zhuǎn)換的公共方法類,結(jié)合完整實例形式分析了C#針對各種常見日期格式的轉(zhuǎn)換方法,涉及C#字符串、日期、時間相關(guān)操作技巧,需要的朋友可以參考下2017-01-01