URLhttps://learnscript.net/zh-hant/programming/operations/locks/
    複製連結移至說明  範例

    鎖定,共用鎖定,獨占鎖定,死結介紹

    閱讀 5:10·字數 1554·發佈 
    Youtube 頻道
    訂閱 375

    鎖定

    鎖定是一種平行狀態下確保業務邏輯正常運轉的機製,其出現的根本原因是平行帶來的執行順序的不確定性,當同一個資源被施加了順序不確定的程式碼邏輯後,出現錯誤將是可以預期的。在不同的系統或語言環境中,鎖定的規則和具體實作會有所不同,這主要取決於需要對資源進行何種形式和程度的存取控製。

    雖然規則和具體實作不會完全一致,但鎖定的使用流程是基本相同的,在某些操作執行之前,必須使用一種權杖要求相關鎖定,只有成功取得,操作才能繼續,當操作完成時,持有的鎖定將被釋放。

    鎖定和多執行緒之間的關系

    如上所述,鎖定之所以被使用是由於對共用資源的平行存取,因此鎖定和多執行緒並沒有必然的聯系,雖然他是鎖定常見的應用案例。當你以某種方式實作了單執行緒中某一資源的平行存取時,鎖定也可以被用在單執行緒中。

    共用鎖定

    共用鎖定允許被多個目標同時持有,是拒絕授予獨占鎖定的依據,當某個權杖已被用於共用鎖定時,使用該權杖申請獨占鎖定將是不可行的。

    由於共用鎖定可以被共同持有,因此其對應的業務邏輯一般不包含對同一資源的寫入操作,否則可能會造成資訊的錯亂。

    獨占鎖定

    獨占鎖定只允許被一個目標持有,是拒絕授予共用鎖定或另一個獨占鎖定的依據,當某個權杖被用於獨占鎖定後,不能再使用該權杖申請共用鎖定或另一個獨占鎖定。

    由於獨占鎖定僅允許單獨持有,因此在其對應的業務邏輯中執行寫入操作是安全的,當然,先決條件是同一資源的所有寫入操作均運用了鎖定機製。

    在下面的 C# 程式碼中,我們將變數locker作為權杖,並使用獨占鎖定限製變數balance的存取,當新的執行緒通過函式Pay修改balance時,主執行緒呼叫的函式GetBalance將陷入等候。

    *.cs
    // 余額,初始值為 1000
    int balance = 1000;
    // 用來要求鎖定的權杖
    object locker = new();
    
    // 函式 Pay,進行支付操作 void Pay(int amount) { // 要求獨占鎖定,確保只有一個目標正在存取 balance lock (locker) { balance -= amount; // 修改余額後等候 5 秒 Thread.Sleep(5000); } }
    // 取得目前的余額 int GetBalance() { // 要求獨占鎖定,確保只有一個目標正在存取 balance lock (locker) return balance; }
    Console.WriteLine("開啟一個執行緒,支付 100"); Thread thread = new(() => Pay(100)); thread.Start();
    Console.WriteLine("現在我要檢視余額!"); // 呼叫 GetBalance 將陷入等候,因為此時 Pay 方法持有鎖定 Console.WriteLine($"余額:{GetBalance()}");
    開啟一個執行緒,支付 100
    現在我要檢視余額!
    約 5 秒後…
    余額:900

    死結

    所謂的死結,是指多個目標都在等候對方先釋放持有的鎖定,導致他們均無法繼續執行。在程式碼邏輯可以任意書寫的情況下,死結總是有可能發生,除非設定一些特殊的規則,比如,目標同一時間只能持有一個鎖定,在要求新的鎖定之前,必須釋放舊的鎖定。當然,如果真的按照這種規則來書寫程式碼,那麽某些業務需求將無法被滿足。

    在下面的 C# 程式碼中,兩個執行緒在持有自己的獨占鎖定後,開始要求對方持有的鎖定,這導致了死結的發生。

    *.cs
    // 用來要求鎖定的權杖
    object lockerA = new();
    object lockerB = new();
    
    // 執行緒 I 先後使用權杖 lockerA,lockerB 取得獨占鎖定 new Thread(() => { lock (lockerA) { Console.WriteLine("執行緒 I 已經取得鎖定 A"); Thread.Sleep(2000);
    Console.WriteLine("執行緒 I 嘗試取得鎖定 B"); lock (lockerB) Console.WriteLine("執行緒 I 已經取得鎖定 B"); } }).Start();
    // 執行緒 II 先後使用權杖 lockerB,lockerA 取得獨占鎖定 new Thread(() => { lock (lockerB) { Console.WriteLine("執行緒 II 已經取得鎖定 B"); Thread.Sleep(2000);
    Console.WriteLine("執行緒 II 嘗試取得鎖定 A"); lock (lockerA) Console.WriteLine("執行緒 II 已經取得鎖定 A"); } }).Start();
    執行緒 I 已經取得鎖定 A
    執行緒 II 已經取得鎖定 B
    約 2 秒後…
    執行緒 I 嘗試取得鎖定 B
    執行緒 II 嘗試取得鎖定 A

    原始碼

    locks.cs·codebeatme/programming·GitHub