天天看點

C#中lock死鎖執行個體教程

http://www.jb51.net/article/54309.htm

在c#中有個關鍵字lock,它的作用是鎖定某一代碼塊,讓同一時間隻有一個線程通路該代碼塊,本文就來談談lock關鍵字的原理和其中應注意的幾個問題:

lock的使用原型是:

?

1

2

3

4

lock

(X)

{

//需要鎖定的代碼....

}

首先要明白為什麼上面這段話能夠鎖定代碼,其中的奧妙就是X這個對象,事實上X是任意一種引用類型,它在這兒起的作用就是任何線程執行到lock(X)時候,X需要獨享才能運作下面的代碼,若假定現在有3個線程A,B,C都執行到了lock(X)而ABC因為此時都占有X,這時ABC就要停下來排個隊,一個一個使用X,進而起到在下面的代碼塊内隻有一個線程在運作(因為此時隻有一個線程獨享X,其餘兩個在排隊),是以這個X必須是所有要執行臨界區域代碼程序必須共有的一個資源,進而起到抑制線程的作用。

下面再來談談lock使用中會遇到和注意的問題,lock最需要注意的一個問題就是線程死鎖!

在MSDN上列出了3個典型問題:

通常,應避免鎖定 public 類型,否則執行個體将超出代碼的控制範圍。常見的結構 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 違反此準則:

如果執行個體可以被公共通路,将出現 lock (this) 問題。

如果 MyType 可以被公共通路,将出現 lock (typeof (MyType)) 問題。

由于程序中使用同一字元串的任何其他代碼将共享同一個鎖,是以出現 lock(“myLock”) 問題。

最佳做法是定義 private 對象來鎖定, 或 private shared 對象變量來保護所有執行個體所共有的資料。

(1)lock (this) 問題:

假定有兩個類: 

class

A{}

class

B{}

有兩個公共對象: 

A a=

new

A();

B b=

new

B();

首先在A中若有一函數内的代碼需要鎖定:

代碼1: 

5

6

7

8

lock

(

this

)

//this在這裡就是a

{

//....

lock

(b)

{

//......

}

}

然而此時B中某函數也有如下代碼需要鎖定:

代碼2: 

lock

(

this

)

//this在這裡就是b

{

//....

lock

(a)

{

//......

}

}

設想一下上面兩段代碼在兩個線程下同時執行會有什麼後果?

結果就是,代碼1執行到lock(this)後a被鎖定,代碼2執行到lock(this)後b被鎖定,然後代碼1需求b,代碼2需求a,此時兩個需求都被互相占有出現僵持狀态,程式死鎖了。

(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,彼此互相占有,彼此僵持,程式進入死鎖狀态!

(3)字元串問題 :

在闡述這個問題之前,有一個知識大家必須知道:C#中字元串被公共語言運作庫 (CLR)“暫留”。這意味着整個程式中任何給定字元串都隻有一個執行個體,就是這同一個對象表示了所有運作的應用程式域的所有線程中的該文本。是以,隻要在應用程式程序中的任何位置處具有相同内容的字元串上放置了鎖,就将鎖定應用程式中該字元串的所有執行個體。

言下之意就是假定有兩個類分别有兩個字元串:

9

10

11

class

A

{

string

a=

"abc"

;

string

b=

"def"

;

}

class

c

{

string

c=

"abc"

;

string

d=

"def"

;

}

事實上a和c引用的是同一個字元串"abc",b和d引用的是同一個字元串"def"

現在如果在兩個類中有如下代碼

在類A中有代碼5: 

lock

(b)

//b是"def"

{

//....

lock

(a)

//a是"abc"

{

//......

}

}

在類B中有代碼6: 

lock

(c)

//c是"abc"

{

//....

lock

(d)

//d是"def"

{

//......

}

}

那麼代碼5和代碼6同時有兩個線程執行結果可想而知:在兩個線程執行到外層lock代碼時"def"和"abc"被鎖定。接着他們在内部lock處同時需求"abc"和"def",而此時兩個字元串被兩個程序彼此占有,程式又死鎖了!是以MSDN說:鎖定字元串尤其危險!最好不要使用!

MSDN最後說了:最佳做法是定義 private 對象來鎖定, 或 private shared 對象變量來保護所有執行個體所共有的資料。 

在個人看來,也不是絕對安全,這裡就舉出一個例子:

假定有一個類: 

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

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)

{

//....

}

}

}

}

現在假定有兩個線程同時執行函數x()和y();結果private對象a和b分别在外層lock鎖定,接着兩個線程在内部又立馬需求b和a,a,b彼此占有又彼此需求,程式死鎖。

是以具體要看情況而定,但是定義 private 對象來鎖定至少可以降低風險。

希望本文所述C#中lock的應用對大家的C#程式設計有所幫助。

lock

(X)

{

//需要鎖定的代碼....

}

class

A{}

class

B{}

A a=

new

A();

B b=

new

B();

lock

(

this

)

//this在這裡就是a

{

//....

lock

(b)

{

//......

}

}

lock

(

this

)

//this在這裡就是b

{

//....

lock

(a)

{

//......

}

}

int

a;

float

b;

lock

(

typeof

(a))

//typeof(a)就是System.type.Int類型

{

//....

lock

(

typeof

(b))

{

//......

}

}

lock

(

typeof

(b))

//typeof(b)就是System.type.Float類型

{

//....

lock

(

typeof

(a))

{

//......

}

}

class

A

{

string

a=

"abc"

;

string

b=

"def"

;

}

class

c

{

string

c=

"abc"

;

string

d=

"def"

;

}

lock

(b)

//b是"def"

{

//....

lock

(a)

//a是"abc"

{

//......

}

}

lock

(c)

//c是"abc"

{

//....

lock

(d)

//d是"def"

{

//......

}

}

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)

{

//....

}

}

}

}