深入淺出多執行緒系列之四 簡單的同步 lock

2022-02-18 07:16:17 字數 3387 閱讀 9198

1: 考慮下下面的**:

class

threadunsafe

_val2 =0

;}}這段**是非執行緒安全的,假設有兩個執行緒

a,b,

a,b都執行到了

go方法的

if判斷中,假設

_val2=1.

所以兩個執行緒

a,b都通過

if判斷,

a執行了

console.writeline

方法,然後退出

if語句,執行

_val2=0,

此時_val2=0.

但是此時執行緒

b才剛剛執行到

console.writeline

方法,而此時

_val2=0.

所以你有可能會得到乙個

divide by zero

的異常。

為了保證執行緒安全,我們可以使用

lock

關鍵字,例如:

static

readonly

object

_locker 

=new

object

();static

int_val1 =1

, _val2 =1

;internal

static

void

go()

_val2 =0

;}}此時執行緒a,

b都只能有乙個可以獲得

_locker

鎖,所以只能有乙個執行緒來執行

lock

塊的**。c#的

lock

關鍵字實際上是

monitor.enter,

和monitor.exit

的縮寫。例如上面的**和下面的等價。

monitor.enter(_locker);

try_val2 =0

;}finally

如果在呼叫

monitor.exit

之前沒有呼叫

monitor.enter

,則會丟擲乙個異常。

不知道大家注意到沒有,在

monitor.enter

和try

方法之間可能會丟擲異常。

abort

,或者是

outofmemoryexception

。為了解決這個問題

clr 4.0

提供了monitor.enter

的過載,增加了

locktaken

字段,當

monitor.enter

成功獲取鎖之後,

locktaken

就是true

,否則為

false

。我們可以將上面的**改成下面的版本。

bool

locktaken 

=false

;try

finally

}monitor

也提供了

tryenter

方法,並且可以傳遞乙個超時時間。如果方法返回

true,

則代表獲取了鎖,否則為

false

。2:選擇同步物件。

monitor.enter

方法的引數是乙個

object

型別,所以任何物件都可以是同步物件,考慮下下面的**:

int i=5; lock(i){}         //

鎖定值型別

lock(this){}           //

鎖定this

物件lock(typeof(product)){}      //

鎖定type

物件。string str="dddd"; lock(str){}   //

鎖定字串

1:鎖定值型別會將值型別進行裝箱,所以

monitor.enter

進入的是乙個物件,但是

monitor.exit()

退出的是另乙個不同的物件。2,

3:鎖定

this

和type

物件,會導致無法控制鎖的邏輯,並且它很難保證不死鎖和頻繁的阻塞,在相同程序中鎖定

type

物件會穿越應用程式域。

4:由於字串駐留機制,所以也不要鎖定

string,

關於這點,請大家去逛一逛老

a的部落格。

3:巢狀鎖:

同乙個執行緒可以多次鎖定同一物件。例如

lock

(locker)

lock

(locker)

lock

(locker)

或者是:

monitor.enter(locker); monitor.enter(locker); monitor.enter(locker);

//do something.

monitor.exit(locker); monitor.exit(locker); monitor.exit(locker);

當乙個執行緒使用乙個鎖呼叫另一方法的時候,巢狀鎖就非常的有用。例如:

static

readonly

object

_locker 

=new

object

();static

void

main()

}static

void

anothermethod()

}4:死鎖:

先看下面的**:

static

object

locker1 

=new

object

();static

object

locker2 

=new

object

();public

static

void

mainthread()

}}).start();

lock

(locker2) //獲取鎖locker2}}

在這裡主線程先獲取

locker2

的鎖,然後

sleep,

接著嘗試獲取

locker1

的鎖。副執行緒先獲取

locker1

的鎖,然後

sleep

,接著嘗試獲取

locker2

的鎖。程式進入了死鎖狀態,兩個執行緒都在等待對方釋放自己等待的鎖。

clr作為乙個獨立宿主環境,它不像

sql server

一樣,它沒有自動檢測死鎖機制,也不會結束乙個執行緒來破壞死鎖。死鎖的執行緒會導致部分執行緒無限的等待。

下篇文章會介紹一些其他同步構造。

深入淺出多執行緒系列之三 執行緒池

執行緒池 每乙個執行緒缺省會被分配 1mb的記憶體,在 c 中,這些都是實打實的分配的,當乙個執行緒啟動的時候,為了分配臨時堆疊大約需要花費幾百微秒的時間。執行緒池通過迴圈利用執行緒可以讓你更高效的利用執行緒。執行緒池就像外包的勞務隊一樣,有任務給他們,他們會管理勞務工的一切,你不需要去花時間去找單...

深入淺出多執行緒系列之三 執行緒池

執行緒池 每乙個執行緒缺省會被分配 1mb的記憶體,在 c 中,這些都是實打實的分配的,當乙個執行緒啟動的時候,為了分配臨時堆疊大約需要花費幾百微秒的時間。執行緒池通過迴圈利用執行緒可以讓你更高效的利用執行緒。執行緒池就像外包的勞務隊一樣,有任務給他們,他們會管理勞務工的一切,你不需要去花時間去找單...

深入淺出多執行緒系列之十二 雙向訊號和競賽

雙向訊號和競賽 two way signaling and races monitor.pulse方法的乙個重要特性是它是非同步執行的,這意味著呼叫pulse方法並不會阻塞自己等待monitor.pulse返回。如果任何乙個執行緒在pulsed 物件上等待,它是不會阻塞的,換句話說,呼叫monito...