你的帳戶有100塊,你拿了金融卡去提100,但很巧的是,你媽也拿了你的本子去提100,如果兩人同時存取同帳戶,且都提款成功,那這個帳戶就變成-100了。
static decimal _balance = 100; private static readonly object _lockObj = new object(); public static void Withdraw(decimal amount) { //lock (_lockObj) //{ if (amount > _balance) { throw new Exception("Insufficient funds"); } _balance -= amount; //} } static void Main(string[] args) { for (int i = 0; i < 5; i++) { Console.WriteLine("========================================"); _balance = 100; var ta = Task.Run(() => { try { Withdraw(100); Console.WriteLine("Task A withdraw"); } catch (Exception ex) { Console.WriteLine($"Task A {ex.Message}"); } }); var tb = Task.Run(() => { try { Withdraw(100); Console.WriteLine("Task B withdraw"); } catch (Exception ex) { Console.WriteLine($"Task B {ex.Message}"); } }); Task.WaitAll(new Task[] { ta, tb }); Console.WriteLine($"balance: {_balance}"); Console.WriteLine("========================================"); } Console.ReadLine(); }試5次左右應該就有機會試出來。
為了防止多緒發生這種情況,我們會使用 Lock 進行保護,lock 的種類依應用細節不同有蠻多的,但最基本款就是
lock(_lockObj) { // critical section }
第一次用 lock 很容易發生的誤會是以為 lock 的保護對象是資源,但實際上 lock 保護的對象是存取該資源的程式碼(critical section),如前 Demo 解註解,可以看到 lock {} 中的判斷領錢與實際扣錢的程式碼才是其保護對象。
整體來說可以想成這樣
至於 _lockObj 就只是要有個變數可以讓 lock statement 標現在有沒有 thread 已經進來了,細節參考: lock。
沒有留言:
張貼留言