天天看點

[C#基礎]說說lock到底鎖誰?

最近一個月一直在弄檔案傳輸元件,其中用到多線程的技術,但有的地方确實需要隻能有一個線程來操作,如何才能保證隻有一個線程呢?首先想到的就是鎖的概念,最近在我們項目組中聽的最多的也是鎖誰,如何鎖?看到有同僚使用lock(this),也有lock(private static object),那就有點困惑了,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方法,可以這樣猜測“直到該對象被釋放”,”該對象“應該是指鎖的對象,對象釋放了或者對象改變了,其他的線程才可以進入代碼臨界區(是不是可以這樣來了解?)。

在多線程中,每個線程都有自己的資源,但是代碼區是共享的,即每個線程都可以執行相同的函數。這可能帶來的問題就是幾個線程同時執行一個函數,導緻資料的混亂,産生不可預料的結果,是以我們必須避免這種情況的發生。

打個比方,有這樣一個情景,很多公司所在的大廈的廁所的蹲位都是小單間型的,也就是一次隻能進去一個人,那麼為了避免每次進去一個人,那怎麼做呢?不就是一個人進去之後順手把門鎖上麼?這樣你在裡面幹啥事,外邊的人也隻能等待你解放完了,才能進入。而蹲位的資源(蹲位,手紙等)是共享的。

最常使用的鎖是如下格式的代碼段:

為什麼鎖的對象是私有的呢?還是以廁所為例子吧,私有就好比,這把鎖隻有你能通路到,而且最好這把鎖不會因為外力而有所改變,别人通路不到,這樣才能保證你進去了,别人就進不去了,如果是公有的,就好比你蹲位小單間的鎖不是安裝在裡面而是安裝在外邊的,别人想不想進就不是你所能控制的了,這樣也不安全。

通過字面的意思就是鎖的目前執行個體對象。那是否對其他執行個體對象産生影響?那下面看一個例子:

[C#基礎]說說lock到底鎖誰?
[C#基礎]說說lock到底鎖誰?

如果在不加鎖的情況下輸出如下:

[C#基礎]說說lock到底鎖誰?

從上面的輸出結果也可以看出,線程出現了争搶的現象,而這并不是我們想要的結果,我們想要的是,每次隻有一個線程去執行print方法。那我們就嘗試一下lock(this)

[C#基礎]說說lock到底鎖誰?
[C#基礎]說說lock到底鎖誰?

輸出結果

[C#基礎]說說lock到底鎖誰?

從輸出結果,覺得大功告成了,可是現在情況又來了,在項目中的其他的地方,有同僚也這樣寫了這樣的代碼,又建立了一個test對象,而且他也知道使用多線程執行耗時的工作,那麼就會出現類似下面的代碼。

[C#基礎]說說lock到底鎖誰?
[C#基礎]說說lock到底鎖誰?

這裡為test加了一個age屬性,為了差別目前建立的對象不是同一個對象。

輸出的結果為

[C#基礎]說說lock到底鎖誰?

在輸出的結果中已經出現了線程搶占執行的情況了,而不是一個線程執行完另一個線程在執行。

那麼我們現在使用一個全局的私有的對象試一試。

[C#基礎]說說lock到底鎖誰?
[C#基礎]說說lock到底鎖誰?

輸出的結果

[C#基礎]說說lock到底鎖誰?

從輸出的結果也可以看出,有序的,每次進來一個線程執行。

那通過上面的比較可以有這樣的一個結論,lock的結果好不好,還是關鍵看鎖的誰,如果外邊能對這個誰進行修改,lock就失去了作用。是以一般情況下,使用靜态的并且是隻讀的對象。

也就有了類似下面的代碼

 你可能會說,不對啊,你下面的代碼跟上面的代碼不一樣啊,為什麼就得出這樣的結論?難道就不能把object放在test類中麼,放在test類中的話,在new test()的時候,其實放在test中也是可以的,隻要保證objlock在外部是無法修改的就可以。

上面說的最多的是lock對象,那麼它能不能lock值類型?

答案是否定的,如

[C#基礎]說說lock到底鎖誰?

當然lock(null)也是不行的,如圖

[C#基礎]說說lock到底鎖誰?

雖然編譯可以通過,但是運作就會出錯。

string也是應用類型,從文法上來說是沒有錯的。

但是鎖定字元串尤其危險,因為字元串被公共語言運作庫 (clr)“暫留”。 這意味着整個程式中任何給定字元串都隻有一個執行個體,就是這同一個對象表示了所有運作的應用程式域的所有線程中的該文本。是以,隻要在應用程式程序中的任何位置處具有相同内容的字元串上放置了鎖,就将鎖定應用程式中該字元串的所有執行個體。通常,最好避免鎖定 public 類型或鎖定不受應用程式控制的對象執行個體。例如,如果該執行個體可以被公開通路,則 lock(this) 可能會有問題,因為不受控制的代碼也可能會鎖定該對象。這可能導緻死鎖,即兩個或更多個線程等待釋放同一對象。出于同樣的原因,鎖定公共資料類型(相比于對象)也可能導緻問題。而且lock(this)隻對目前對象有效,如果多個對象之間就達不到同步的效果。lock(typeof(class))與鎖定字元串一樣,範圍太廣了。

關于lock的介紹就到這裡,有下面幾點需要注意的地方

1、lock的是引用類型的對象,string類型除外。

2、lock推薦的做法是使用靜态的、隻讀的、私有的對象。

3、保證lock的對象在外部無法修改才有意義,如果lock的對象在外部改變了,對其他線程就會暢通無阻,失去了lock的意義。

參考文章

http://www.cnblogs.com/jintianhu/archive/2010/11/19/1881494.html

部落格位址:

<a href="http://www.cnblogs.com/wolf-sun">http://www.cnblogs.com/wolf-sun/</a>

部落格版權:

本文以學習、研究和分享為主,歡迎轉載,但必須在文章頁面明顯位置給出原文連接配接。

如果文中有不妥或者錯誤的地方還望高手的你指出,以免誤人子弟。如果覺得本文對你有所幫助不如【推薦】一下!如果你有更好的建議,不如留言一起讨論,共同進步!

再次感謝您耐心的讀完本篇文章。http://www.cnblogs.com/wolf-sun/p/4209521.html

繼續閱讀