1. 程式人生 > 其它 >[MethodImpl(MethodImplOptions.Synchronized)]與lock機制

[MethodImpl(MethodImplOptions.Synchronized)]與lock機制

[MethodImpl(MethodImplOptions.Synchronized)]與lock機制

在進行.NET開發時,經常會遇見如何保持執行緒同步的情況。在眾多的執行緒同步的可選方式中,加鎖無疑是最為常用的。如果僅僅是基於方法級別的執行緒同步,使用System.Runtime.CompilerServices.MethodImplAttribute無疑是最為簡潔的一種方式。MethodImplAttribute可以用於instance method,也可以用於static method。當在某個方法上標註了MethodImplAttribute,並指定MethodImplOptions.Synchronized引數,可以確保在不同執行緒中執行的該方式以同步的方式執行。

查閱MSDN的說明:

The method can be executed by only one thread at a time. Static methods lock on the type, whereas instance methods lock on the instance. Only one thread can execute in any of the instance functions, and only one thread can execute in any of a class's static functions. 這個方法一次只能執行一個執行緒。靜態方法鎖定型別,而例項方法鎖定例項。只有一個執行緒可以在任何一個例項函式中執行,而且只有一個執行緒可以在任何一個類的靜態函式中執行。

可以看出:

  • [MethodImplAttribute(MethodImplOptions.Synchronized)]仍然是採用加鎖的機制實現執行緒的同步。
  • 如果它被應用到instance method,相當於對當前例項加鎖。
  • 如果它被應用到static method,相當於當前型別加鎖

可參考文章:

[MethodImpl(MethodImplOptions.Synchronized)]、lock(this)與lock(typeof(...))

  • lock機制

關鍵字lock的作用是鎖定某一程式碼塊,讓同一時間只有一個執行緒訪問該程式碼塊。

lock(X) { //需要鎖定的程式碼....
}

那麼為什麼上面這段話能夠鎖定程式碼?其中的奧妙就是X這個物件,事實上X是任意一種引用型別,它在這兒起的作用就是任何執行緒執行到lock(X)時候,X需要獨享才能執行下面的程式碼,若假定現在有3個執行緒A,B,C都執行到了lock(X)而ABC因為此時都佔有X,這時ABC就要停下來排個隊,一個一個使用X,從而起到在下面的程式碼塊內只有一個執行緒在執行(因為此時只有一個執行緒獨享X,其餘兩個在排隊),所以這個X必須是所有要執行臨界區域程式碼程序必須共有的一個資源,從而起到抑制執行緒的作用。

lock最需要注意的一個問題就是執行緒死鎖,MSDN上列出了3個典型問題:

通常,應避免鎖定 public 型別,否則例項將超出程式碼的控制範圍。常見的結構 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 違反此準則:

  1. 如果例項可以被公共訪問,將出現 lock (this) 問題。
  2. 如果 MyType 可以被公共訪問,將出現 lock (typeof (MyType)) 問題。
  3. 由於程序中使用同一字串的任何其他程式碼將共享同一個鎖,所以出現 lock(“myLock”) 問題。

最佳做法是定義 private 物件來鎖定, 或 private shared 物件變數來保護所有例項所共有的資料。

  • lock的內涵(Monitor類)

另外,使用lock來實現C#執行緒同步,在C#編譯器編譯lock語句時,將其編譯成了呼叫Monitor類。一條lock語句會被編譯成呼叫Monitor的Enter和Exit方法。Monitor在 System.Threading命名空中。lock的功能就相當於直接呼叫Monitor的Entry方法,所不同的是,lock方法在結束後,會自動解除鎖定,當然,在IL中是呼叫了Monitor的Exit方法,但在C#程式中,看起來是自動解鎖的,這類似於C#中的using語句,可以自動釋放資料庫等資源。但如果直接在C#源程式中使用Monitor類,就必須呼叫Exit方法來顯式地解除鎖定。

示例:

Monitor.Enter(lockObj); try { // 程式碼 } catch(Exception e) { // 異常處理程式碼 } finally { Monitor.Exit(lockObj); // 解除鎖定 }

Exit方法最後在finally裡呼叫,這樣無論在方法在發生異常、返回還是正常執行,都會執行到finally,並呼叫Exit方法解除鎖定。

Monitor類不僅可以完全取代lock語句,還可以使用TryEntry方法設定一個鎖定超時。

示例:

if(Monitor.TryEntry(lockObj, 1000)) { try { } finally { Monitor.Exit(lockObj); } } else { // 超時後的處理程式碼 }

設定鎖定超時時間為1秒,即在1秒中後,lockObj還未被解鎖,TryEntry方法就會返回false,如果在1秒之內,lockObj被解鎖,TryEntry返回true。這種方法對於避免死鎖提供了一種不錯的思路。

程式設計是個人愛好