lock與Monitor
阿新 • • 發佈:2022-03-01
應用場景
lock 確保當一個執行緒位於程式碼的臨界區時,另一個執行緒不進入臨界區。如果其他執行緒試圖進入鎖定的程式碼,則它將一直等待(即被阻止),直到該物件被釋放。
lock語句根本使用的就是Monitor.Enter
和Monitor.Exit
,也就是說lock(this)時執行Monitor.Enter(this),大括號結束時執行Monitor.Exit(this)。
應用場景:經常會應用於防止多執行緒操作導致公用變數值出現不確定的異常,用於確保操作的安全性,比如搶購。
鎖等於“行為可以預期”,不鎖等於“行為不可預期”。
搶購舉例
假設有商品庫存為10,共有1000名顧客在幾乎同一時間進行搶購。
不加lock的寫法:
Parallel.For(0, customerCount, (i) =>
{
TryToBuyGoods(customerCount);
});
加lock的寫法:(實際上轉為單執行緒)
Parallel.For(0, customerCount, (i) =>
{
lock (inStockLock)
{
TryToBuyGoods(customerCount);
}
});
加Monitor的寫法:(實際上轉為單執行緒)
Parallel.For(0, customerCount, (i) => { bool lockWasTaken = false; try { System.Threading.Monitor.Enter(inStockLock, ref lockWasTaken); TryToBuyGoods(customerCount); } finally { if (lockWasTaken) System.Threading.Monitor.Exit(inStockLock); } });
private void TryToBuyGoods(int customerCount) { var buyCustomer = Customers[random.Next(0, customerCount)]; var sleepTime = random.Next(1000, 10000); if (inStock > 0) { //模擬購物業務邏輯處理時間(佔用資源) Thread.Sleep(sleepTime); inStock--; //上述流程是先處理一系列業務邏輯,後減庫存,在不加lock的情況下會出現超賣現象 //建議的邏輯是,先減庫存-處理業務邏輯-後續庫存不足,恢復庫存,並提示使用者“購買失敗” Console.WriteLine($"顧客{buyCustomer.Name}購買了一件商品"); } //Console.WriteLine($"當前庫存:{inStock}"); }
結果如下:
可以看出,這種情況不加lock是非常危險的事情。
示例程式碼
Monitor的其他用法
private static void WriteToResource(Random rnd, int timeout)
{
try
{
//嘗試獲得鎖
if(Monitor.TryEnter(locker, timeout))
{
resource = rnd.Next(500);
Display("writes resource value " + resource);
writes++;
}
else
{
//獲取失敗
Display("can not writes resource value.");
}
}
catch
{
}
finally
{
//如果獲得了鎖,需要釋放
if(Monitor.IsEntered(locker))
Monitor.Exit(locker);
}
}
示例程式碼
參考資料
學習技術最好的文件就是【官方文件】,沒有之一。
還有學習資料【Microsoft Learn】、【CSharp Learn】、【My Note】。
如果,你認為閱讀這篇部落格讓你有些收穫,不妨點選一下右下角的【推薦】按鈕。
如果,你希望更容易地發現我的新部落格,不妨點選一下【關注】。