寫在前面
最近一個月一直在弄文件傳輸組件,其中用到多線程的技術,但有的地方確實需要只能有一個線程來操作,如何才能保證只有一個線程呢?首先想到的就是鎖的概念,最近在我們項目組中聽的最多的也是鎖誰,如何鎖?看到有同事使用lock(this),也有lock(private static object),那就有點困惑了,lock到底鎖誰才是最合適的呢?
lock
首先先上官方Msdn的說法
lock 關鍵字可確保當一個線程位於代碼的臨界區時,另一個線程不會進入該臨界區。 如果其他線程嘗試進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放。
lock 關鍵字在塊的開始處調用 Enter,而在塊的結尾處調用 Exit。 ThreadInterruptedException 引發,如果 Interrupt 中斷等待輸入 lock 語句的線程。
通常,應避免鎖定 public 類型,否則實例將超出代碼的控制范圍。常見的結構 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 違反此准則:
如果實例可以被公共訪問,將出現 lock (this) 問題。
如果 MyType 可以被公共訪問,將出現 lock (typeof (MyType)) 問題。
由於進程中使用同一字符串的任何其他代碼都將共享同一個鎖,所以出現 lock("myLock") 問題。
最佳做法是定義 private 對象來鎖定, 或 private static 對象變量來保護所有實例所共有的數據。
在 lock 語句的正文不能使用 等待 關鍵字。
Enter指的是Monitor.Enter(獲取指定對象上的排他鎖。),Exit指的是Monitor.Exit(釋放指定對象上的排他鎖。)
有上面msdn的解釋及Exit方法,可以這樣猜測“直到該對象被釋放”,”該對象“應該是指鎖的對象,對象釋放了或者對象改變了,其他的線程才可以進入代碼臨界區(是不是可以這樣來理解?)。
在多線程中,每個線程都有自己的資源,但是代碼區是共享的,即每個線程都可以執行相同的函數。這可能帶來的問題就是幾個線程同時執行一個函數,導致數據的混亂,產生不可預料的結果,因此我們必須避免這種情況的發生。
打個比方,有這樣一個情景,很多公司所在的大廈的廁所的蹲位都是小單間型的,也就是一次只能進去一個人,那么為了避免每次進去一個人,那怎么做呢?不就是一個人進去之后順手把門鎖上么?這樣你在里面干啥事,外邊的人也只能等待你解放完了,才能進入。而蹲位的資源(蹲位,手紙等)是共享的。
最常使用的鎖是如下格式的代碼段:
private static object objlock = new object(); lock (objlock ) { //要執行的代碼邏輯 }
為什么鎖的對象是私有的呢?還是以廁所為例子吧,私有就好比,這把鎖只有你能訪問到,而且最好這把鎖不會因為外力而有所改變,別人訪問不到,這樣才能保證你進去了,別人就進不去了,如果是公有的,就好比你蹲位小單間的鎖不是安裝在里面而是安裝在外邊的,別人想不想進就不是你所能控制的了,這樣也不安全。
lock
原文章列舉的demo無法說明問題,已將原文章的demo刪除,將修改后的demo遷移到這篇文章。
上面說的最多的是lock對象,那么它能不能lock值類型?
答案是否定的,如
當然lock(null)也是不行的,如圖
雖然編譯可以通過,但是運行就會出錯。
lock(string)
string也是應用類型,從語法上來說是沒有錯的。
但是鎖定字符串尤其危險,因為字符串被公共語言運行庫 (CLR)“暫留”。 這意味着整個程序中任何給定字符串都只有一個實例,就是這同一個對象表示了所有運行的應用程序域的所有線程中的該文本。因此,只要在應用程序進程中的任何位置處具有相同內容的字符串上放置了鎖,就將鎖定應用程序中該字符串的所有實例。通常,最好避免鎖定 public 類型或鎖定不受應用程序控制的對象實例。例如,如果該實例可以被公開訪問,則 lock(this) 可能會有問題,因為不受控制的代碼也可能會鎖定該對象。這可能導致死鎖,即兩個或更多個線程等待釋放同一對象。出於同樣的原因,鎖定公共數據類型(相比於對象)也可能導致問題。而且lock(this)只對當前對象有效,如果多個對象之間就達不到同步的效果。lock(typeof(Class))與鎖定字符串一樣,范圍太廣了。
總結
關於lock的介紹就到這里,有下面幾點需要注意的地方
1、lock的是引用類型的對象,string類型除外。
2、lock推薦的做法是使用靜態的、只讀的、私有的對象。
3、保證lock的對象在外部無法修改才有意義,如果lock的對象在外部改變了,對其他線程就會暢通無阻,失去了lock的意義。
@Aulan
this 表示的就是當前實例,當你再new一個的時候,鎖定的就不再是同一個對象了。 不能鎖定值類型的原因是,當這個值類型傳遞到另一個線程的時候,會創建一個副本,鎖定的也不再是同一個對象了。 鎖定字符串帶來的問題是,字符串在CLR中會暫存在 內存中,如果有兩個變量被分配了相同的字符串內容,那么這兩個引用會指向同一塊內存,實際鎖定也就是同一個對象,這就會導致整個應用程序的阻塞。所以鎖定字符串是非常危險的行為。
參考文章
http://www.cnblogs.com/jintianhu/archive/2010/11/19/1881494.html