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...