1. 程式人生 > 其它 >C# Lock關鍵字

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“暫留”。即整個程式中任何給定字串都只有一個例項,具有相同內容的字串都代表著同一個例項。因此,只要在應用程式程序中的任何位置處具有相同內容的字串上放置了鎖,就將鎖定應用程式中與該字串具有相同內容的字串。因此,最好鎖定不會被暫留的私有或受保護成員。

例項:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;

public class test { private static object lock_obj = new object(); string str; string path; public test(string str, string path) { this.str = str; this.path = path; } public void Pri() { lock (str) { for (int i = 0; i < 10; i++) { Debug.Log(path + i); } } } } public class ApplicationStart : MonoBehaviour { // Start is called before the first frame update void Start() { string str1 = "ssss"; string str2 = "ssss"; test t1 = new test(str1, "這是執行緒一"); Thread thread1 = new Thread(new ThreadStart(t1.Pri)); thread1.Start(); test t2 = new test(str2, "這是執行緒二"); Thread thread2 = new Thread(new ThreadStart(t2.Pri)); thread2.Start(); } }

結果:

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

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

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

例項:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;

public class test
{
    private static object lock_obj = new object();
    string str;
    string path;

    public test(string str, string path)
    {
        this.str = str;
        this.path = path;
    }
    public void Pri()
    {
        lock (lock_obj)
        {
            for (int i = 0; i < 10; i++)
            {
                Debug.Log(path + i);
            }
        }
    }
}
public class ApplicationStart : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        string str1 = "ssss";
        string str2 = "ssss";
        test t1 = new test(str1, "這是執行緒一");
        Thread thread1 = new Thread(new ThreadStart(t1.Pri));
        thread1.Start();
        test t2 = new test(str2, "這是執行緒二");
        Thread thread2 = new Thread(new ThreadStart(t2.Pri));
        thread2.Start();
    }
}

結果:

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

————————————————
版權宣告:本文為CSDN博主「戎夏不姓夏」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/qq_39025293/article/details/84655433