你的帳戶有100塊,你拿了金融卡去提100,但很巧的是,你媽也拿了你的本子去提100,如果兩人同時存取同帳戶,且都提款成功,那這個帳戶就變成-100了。
試5次左右應該就有機會試出來。
- 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();
- }
為了防止多緒發生這種情況,我們會使用 Lock 進行保護,lock 的種類依應用細節不同有蠻多的,但最基本款就是
- lock(_lockObj)
- {
- // critical section
- }
第一次用 lock 很容易發生的誤會是以為 lock 的保護對象是資源,但實際上 lock 保護的對象是存取該資源的程式碼(critical section),如前 Demo 解註解,可以看到 lock {} 中的判斷領錢與實際扣錢的程式碼才是其保護對象。
整體來說可以想成這樣
至於 _lockObj 就只是要有個變數可以讓 lock statement 標現在有沒有 thread 已經進來了,細節參考: lock。
沒有留言:
張貼留言