2009-08-25 131 views

回答

15

爲什麼鎖是一種語言結構,編譯器選擇在表達式上施加額外的語義。 Monitor.Enter只是一個方法調用,C#編譯器不會以任何方式特殊處理調用,因此它會經歷正常的重載解析和裝箱。

0

我會說這是因爲Monitor.Enter()是一個方法調用,所以編譯器自動執行拳,而lock()是一個語法元素,因此編譯器可以檢查和值類型拋出一個錯誤。

+0

我不認爲有任何檢查。 不是鎖只是用try塊中的Monito.Enter和finally塊中的Monitor.Exit進行擴展。 – Sandbox 2009-08-25 18:04:12

+1

不,它被擴展到,但首先編譯器點,如果你是愚蠢的道路上... – ShuggyCoUk 2009-08-25 18:05:29

11

您絕對不能在int上使用Monitor.Enter。它的工作原因是因爲int被裝箱,所以除非您存儲對裝箱值的引用,否則您將鎖定臨時對象,這意味着您不能在沒有發生異常的情況下調用Monitor.Exit

推薦的鎖定方式是創建一個private readonly object並對其進行鎖定。對於靜態方法,您可以使用private static object

3

只是出於好奇,你在做什麼與變量'我',需要它被鎖定?這可能是更有效地使用Interlocked類,如果你所有的做的是增量或東西:

Interlocked.Increment(i); // i++ in a thread safe manner 

互鎖類是重量最輕的線程同步工具.NET提供,以及簡單的遞增,遞減,讀或交換,這是最好的選擇。

如果你試圖行爲的塊同步,那麼我會簡單地創建可被用作同步根的對象:

object syncRoot = new object(); 

// ... 

lock(syncRoot) 
{ 
    // put synced behavior here 
} 
+0

純粹出於興趣......我知道聯鎖班。謝謝。 – Sandbox 2009-08-25 18:11:59

5

用於編譯器中的規範定義the behaviour of lock like so

鎖語句的表達式的編譯時類型應該是已知爲引用類型的引用類型或類型參數(第25.1.1節)。表達式的 編譯時類型表示值類型,這是編譯時錯誤。

然後,它定義了它相當於,只要它編譯

由於監視器。退出只是一個沒有任何約束的方法調用,它不會阻止編譯器自動裝入int並繼續其快樂(而且非常)錯誤的方式。

lock不是簡單的語法糖以相同的方式foreach不是簡單的語法糖。所得到的IL變換是而不是呈現給代碼的其餘部分,就好像那是寫過的那樣。

foreach修改迭代變量是非法的(儘管在結果輸出代碼中沒有任何IL級別會阻止這一點)。在鎖中,編譯器會阻止編譯時間已知的值類型,儘管由此產生的IL不關心這一點。

順便說一句:
理論上編譯器可以「祝福」與此(等)方法成竹在胸,使其看上這種情況出現明顯的情況下,但根本上是不可能總是在發現這一點編譯時間(考慮從另一個方法,程序集或通過反射傳入的對象),因此很難發現任何此類實例可能會產生反效果。

編譯器對API的內部知道得越多,如果您希望將來改變API,您將遇到的問題也越多。

例如,可以添加一個Monitor.Enter()的重載,它可以添加一個int並將其鎖定在與int值關聯的進程寬互斥量上。
這將符合監視器的規格(儘管它可能會很可怕),但會對較老的編譯器造成嚴重問題,仍然會快速地阻止已成爲合法的操作。

+0

很好的答案。謝謝。 – Sandbox 2009-08-25 18:21:09

相關問題