lock與C#多線程
lock 關鍵字將語句塊標記為臨界區,方法是獲取給定對象的互斥鎖,執行語句,然後釋放該鎖。簡單講就類似於 你去銀行辦理業務,一個櫃台一次只能操作以為客戶,而如果你要到這個櫃台辦理業務就必須等前面的人的業務完成,而彼此之間不會有交集。下面通過具體的代碼來深入說明:
using System; using System.Threading; namespace LockTest { class Program { static void Main() { Test test = new Test(); Thread one = new Thread(test.ThreadOne); //線程一調用test的 ThreadOne 方法 Thread two = new Thread(test.ThreadTwo); //線程而調用test的 ThreadTwo 方法 one.Start(); //啟動線程一 two.Start(); //啟動線程二 Console.ReadKey(); } } class LockTest { public int Number { get; set; } = 10; public void Print() { Console.WriteLine("The number is " + Number); } } class Test { private readonly LockTest _lockTest = new LockTest(); //用於測試的對象 public void ThreadOne() { //在此方法內鎖定 _lockTest 所引用的對象並執行相應操作,在操作執行完以前不會釋放次對象 lock (_lockTest) { Console.WriteLine("The object has been locked!"); Thread.Sleep(5000); //讓當前線程休眠 5s _lockTest.Number = 200; Console.Write("ThreadOne: "); _lockTest.Print(); } //操作完成並釋放對象 Console.WriteLine("The object has been released!"); } public void ThreadTwo() {
//Console.WriteLine(_lockTest.Number); _lockTest.Number = 100;
//Console.WriteLine(_lockTest.Number); //鎖定 _lockTest 所引用的對象 //如果要保證 lock 正常工作,所有對 _lockTest 的操作都要使用 lock 鎖定 //比如上面 _lockTest.Number=100; 在 lock 外面,那麼它將不受約束(即可以強制訪問 _lockTest) //如果在上面語句後加 Console.WriteLine(_lockTest.Number); 那麼將輸出 100 而不是 200 (也不是 10) lock (_lockTest) {
//_lockTest.Number=100; Console.Write("ThreadTwo: "); _lockTest.Print(); } } } }
運行上面的代碼會發現在ThreadTwo 方法裡的 lock內的代碼 時有明顯的延遲,即必須等到ThreadOne運行完成了才繼續執行 lock內部的代碼,而且輸出的結果是200而不是100,說明 lock 外面的代碼不會發生任何延遲。如果把 _lockTest.Number=100; 語句放在lock內部,會發現結果變成了 100 。
通過上面的例子可以看出要保證 lock 正確工作,要對每個 _lockTest 的操作加上 lock鎖定 。而在程序運行的時候,會根據線程訪問次對象的先後順序來為每個線程排序,且只有排在前面的線程對對象的操作完成了後面的對象才能訪問此對象。