Review代碼發現有一個方法加了[MethodImpl(MethodImplOptions.Synchronized)] 屬性,這個屬性的目的,從名字上就可以看出,是要對所有線程進行同步執行。
對方法加上這個屬性之后,會把整個方法體加在一個同步塊中,比如下面的代碼:
[MethodImpl(MethodImplOptions.Synchronized)] public static void syncDemo() { if (count % 10 != 0) { Thread.Sleep(50); count++; } }
其實和下面的代碼是一樣的(SyncMethodCls是包含這個方法的類):
public static void lockDemo() { lock (typeof(SyncMethodCls)) { if (count % 10 != 0) { Thread.Sleep(50); count++; } } }
從第二個方法中,可以看到使用[MethodImpl(MethodImplOptions.Synchronized)]這個屬性是對整個類型進行加鎖的,同步塊是整個方法。如果這個類中只有一個靜態同步方法還好,如果有兩個同步靜態方法都使用這個屬性進行標注,這兩個方法之間就會出現競態,在一些情況下,你可能並不想讓兩個不是很相關的方法出現競態的情況。同一個類型中越多這種靜態同步方法,出現的競爭越激勵,系統性能也會越差。
上面也說了,使用這個屬性之后,是對整個方法進行同步,但是有時候有些條件判斷並不需要放在同步塊中,比如上面方法中的if 條件,我並不想放到同步塊中,因為有時候已經滿足條件的線程就不需要再次阻塞了。這種情況在單例中最明顯了,首先判斷單例的實例是否為空,只有為空的時候,才會去加鎖重新生成一個新的實例。
以上方法可以參考單例進行改造,改造后的代碼如下:
private static object syncObj = new object(); public static void syncObjDemo() { if (count % 10 !=0) { lock(syncObj) { if (count % 10 != 0) { Thread.Sleep(50); count++; } } } }
改造之后,競爭數量明顯減少。
下面附上使用[MethodImpl(MethodImplOptions.Synchronized)]競爭情況:
改造之后的競爭情況:
多線程調用情況如下:
for (int i = 0; i <= 1000; i++) { ThreadPool.QueueUserWorkItem((t) => { // syncMethodCls.syncDemo(); syncMethodCls.syncObjDemo(); }); }
到此,需要好好說說這個[MethodImpl(MethodImplOptions.Synchronized)]了,MSDN上這樣解釋:“該方法一次性只能在一個線程上執行。 靜態方法在類型上鎖定,而實例方法在實例上鎖定。 只有一個線程可在任意實例函數中執行,且只有一個線程可在任意類的靜態函數中執行。” 總結一句話就是:靜態方法鎖整個類,實例方法鎖整個實例。這句話乍一看挺嚇人的,但是如果你的類中只有一個同步方法的話,鎖整個類和整個實例影響也不大,但是要確保類和實例在其他地方不會再次被鎖,否則會造成死鎖的。
所以這個屬性,還是盡量不要使用了,不光有可能造成性能問題不說,還有可能造成死鎖的嚴重問題。
說到鎖整個類,最近看java多線程的部分,java中有個和C#的lock類似的同步關鍵字synchronized,好多例子都是直接在方法上加這個關鍵字,實現方法的同步實現。這個關鍵字實現的方式應該類似MethodImplOptions.Synchronized,靜態方法鎖類型,實例方法鎖實例,而且如果有條件判斷可能還會造成不必要的阻塞。而且由於jdk對synchronized的不斷優化,在有些時候並不會馬上進行加鎖,而是會先自旋一會(以通過浪費CPU時間減少阻塞),可能在某些時候造成CPU時間片的浪費。所以使用的時候,也需要注意。