C#Lock機制

 

定義:lock 確保當一個線程位於代碼的臨界區時,另一個線程不進入臨界區。如果其他線程試圖進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放。

Monitor 方法是靜態的,不需要生成Monitor 類的實例就可以直接調用它們。在.NET Framework 中,每個對象都有一個與之關聯的鎖,對象可以得到並釋放它以便於在任意時間只有一個線程可以訪問對象實例變量和方法。

Lock語句可以說就是通過Monitor.Enter()和Monitor.Exit()實現的。

當Lock(lockInstance){}結構開始執行時調用Monitor.Enter(lockInstance)鎖定lockInstance臨界區,當該結構執行結束,調用monitor.Exit(lockInstance)釋放lockInstance臨界區。

原理:對於任何一個對象來說,它在內存中的第一部分放置的是所有方法的地址,第二部分放着一個索引,這個索引指向CLR中的SyncBlock Cache區域中的一個SyncBlock。當你執行Monitor.Enter(Object)時,如果object的索引值爲負數,就從SyncBlock Cache中選取一個SyncBlock,將其地址放在object的索引中。這樣就完成了以object爲標誌的鎖定,其他的線程想再次進行Monitor.Enter(object)操作,將獲得object的已經爲正值的索引,然後就等待。直到索引變爲負數,即調用Monitor.Exit(object)將索引變爲負數,等待的線程開始執行。


注意:

1.lock不能鎖定空值,但Null是不需要被釋放的。
2.lock不能鎖定string類型,雖然它也是引用類型的。因爲字符串類型被CLR“暫留”。即整個程序中任何給定字符串都只有一個實例,具有相同內容的字符串都代表着同一個實例。因此,只要在應用程序進程中的任何位置處具有相同內容的字符串上放置了鎖,就將鎖定應用程序中與該字符串具有相同內容的字符串。因此,最好鎖定不會被暫留的私有或受保護成員。

實例:

測試結果 :

可以看出,str01和str02是兩個string類型的變量,但是當lock(str01)之後,str02變成了臨界區被鎖定導致th2中lock(){}體處於等待狀態。這就對應了只要在應用程序進程中的任何位置處具有相同內容的字符串上放置了鎖,就將鎖定應用程序中與該字符串具有相同內容的字符串這句話。
3.lock鎖定的對象是一個程序塊的內存邊界
4.值類型不能被lock,因爲前文標紅字的“對象被釋放”,值類型不是引用類型的

5.lock就避免鎖定public 類型或不受程序控制的對象。
例如,如果該實例可以被公開訪問,則 lock(this) 可能會有問題,因爲不受控制的代碼也可能會鎖定該對象。這可能導致死鎖,即兩個或更多個線程等待釋放同一對象。出於同樣的原因,鎖定公共數據類型(相比於對象)也可能導致問題。
使用lock(this)的時候,類的成員變量的值可能會被不在臨界區的方法改值了

應用場景:經常會應用於防止多線程操作導致公用變量值出現不確定的異常,用於確保操作的安全性。

實例:

測試結果:

可以看出,當線程th執行lock(stu)之後,stu成爲臨界區被鎖定。其他線程等待該lock(){}執行完成stu被釋放後,開始執行。 

另外,由於我是學習Unity遊戲開發的,這裏插入一句,在Unity中是不允許在子線程中訪問Unity的一些屬性例如Transfrom等。我猜測這是因爲防止因爲子線程的使用不當,導致Unity底層數據的錯亂。所以Unity直接使用Lock機制阻止用戶直接在子線程中使用Unity的一些特定屬性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章