C#線程間互相通信主要用到兩個類:AutoResetEvent和ManualResetEvent。 一、AutoResetEvent AutoResetEvent 允許線程通過發信號互相通信,線程通過調用 AutoResetEvent 上的 WaitOne 來等待信號。 如果 AutoResetEvent 為非終止狀態,則線程會被阻止,並等待當前控制資源的線程通過調用 Set 來通知資源可用。 下面我用吃快餐的例子來說明這個問題,吃快餐的時候都會排隊付款,收銀員發送收款通知,客戶依次付錢,代碼如下: 復制代碼 1 class Program 2 { 3 //若要將初始狀態設置為終止,則為 true;若要將初始狀態設置為非終止,則為 false 4 static AutoResetEvent autoResetEvent = new AutoResetEvent(false); 5 6 static void Main(string[] args) 7 { 8 Thread t1 = new Thread(() => 9 { 10 Console.WriteLine("客戶1在排隊等待付錢..."); 11 12 //客戶1調用AutoResetEvent上的WaitOne來等待付錢通知 13 autoResetEvent.WaitOne(); 14 Console.WriteLine("通知來了,客戶1付錢"); 15 }); 16 t1.IsBackground = true; 17 t1.Start(); 18 19 Pay();//發送通知 20 Console.ReadKey(); 21 } 22 23 static void Pay() 24 { 25 string tip = Console.ReadLine(); 26 if (tip == "next") 27 { 28 autoResetEvent.Set();//收銀員發送付錢通知,通過調用Set來通知客戶付錢 29 } 30 } 31 } 復制代碼 運行在屏幕中打印: 客戶1在排隊等待付錢... 等收銀員說“next”的時候,向客戶1發送付錢通知(autoResetEvent.Set()),屏幕打印: 客戶1在排隊等待付錢... next 通知來了,客戶1付錢! AutoResetEvent類一次只能通知一個等待的線程,且通知一次過後會立即將AutoResetEvent對象的狀態置為false,也就是如果有兩個客戶都在等待收銀員通知,AutoResetEvent對象的set方法只能通知到第一個客戶,代碼和效果如下: 復制代碼 1 class Program 2 { 3 //若要將初始狀態設置為終止,則為 true;若要將初始狀態設置為非終止,則為 false。 4 static AutoResetEvent autoResetEvent = new AutoResetEvent(false); 5 6 static void Main(string[] args) 7 { 8 Thread t1 = new Thread(() => 9 { 10 Console.WriteLine("客戶1在排隊等待付錢..."); 11 12 //客戶1調用AutoResetEvent上的WaitOne來等待付錢通知 13 autoResetEvent.WaitOne(); 14 Console.WriteLine("通知來了,客戶1付錢"); 15 }); 16 t1.IsBackground = true; 17 t1.Start(); 18 19 Thread t2 = new Thread(() => 20 { 21 Console.WriteLine("客戶2在排隊等待付錢..."); 22 23 //客戶2調用AutoResetEvent上的WaitOne來等待付錢通知 24 autoResetEvent.WaitOne(); 25 Console.WriteLine("通知來了,客戶2付錢!"); 26 }); 27 t2.IsBackground = true; 28 t2.Start(); 29 30 Pay();//發送通知 31 32 Console.ReadKey(); 33 } 34 35 static void Pay() 36 { 37 string tip = Console.ReadLine(); 38 if (tip == "next") 39 { 40 autoResetEvent.Set();//收銀員發送付錢通知,通過調用Set來通知客戶付錢 41 } 42 } 43 } 復制代碼 運行後屏幕打印: 客戶1在排隊等待付錢... 客戶1在排隊等待付錢... next 通知來了,客戶1付錢! 這就說明在通知完客戶1後,autoResetEvent 的狀態又被置為了false,這時如果要通知到客戶2,就需要在通知完客戶1後,再執行一次通知,在線程1中加上一行代碼,如下: 復制代碼 1 Thread t1 = new Thread(() => 2 { 3 Console.WriteLine("客戶1在排隊等待付錢..."); 4 5 //客戶1調用AutoResetEvent上的WaitOne來等待付錢通知 6 autoResetEvent.WaitOne(); 7 Console.WriteLine("通知來了,客戶1付錢"); 8 9 autoResetEvent.Set();//讓其再通知下個客戶 10 }); 復制代碼 運行後屏幕打印: 客戶1在排隊等待付錢... 客戶1在排隊等待付錢... next 通知來了,客戶1付錢! 通知來了,客戶2付錢! 這也就說明每調用一次Set,只有一個線程會解除等待,換句話說,有多少個線程就要調用多少次Set,線程才會全部繼續。 二、ManualResetEvent 在AutoResetEvent中,如果要通知兩個線程,則Set方法要被執行兩次,也以快餐店的例子做了舉例,但如果有一天客戶1中彩票了,要請部門的10個同事吃飯,也就是說只要Set一次,所有在等待的線程都會解除等待,相當於收銀員只收一次錢,10個人都可以通過收銀去吃飯,這時我們就要用到ManualResetEvent類,它的用法和AutoResetEvent基本一樣,區別就在於它是一量Set方法發出通知後,要再次阻塞的話就需要手動去修改,也就是調用Reset方法,代碼如下: 復制代碼 1 class Program 2 { 3 //若要將初始狀態設置為終止,則為 true;若要將初始狀態設置為非終止,則為 false。 4 static ManualResetEvent manualResetEvent = new ManualResetEvent(false); 5 6 static void Main(string[] args) 7 { 8 Thread t1 = new Thread(() => 9 { 10 Console.WriteLine("客戶1在排隊等待付錢..."); 11 12 //客戶1調用manualResetEvent上的WaitOne來等待付錢通知 13 manualResetEvent.WaitOne(); 14 Console.WriteLine("已經有人請客,客戶1不用付錢"); 15 }); 16 t1.IsBackground = true; 17 t1.Start(); 18 19 Thread t2 = new Thread(() => 20 { 21 Console.WriteLine("客戶2在排隊等待付錢..."); 22 23 //客戶2調用manualResetEvent上的WaitOne來等待付錢通知 24 manualResetEvent.WaitOne(); 25 Console.WriteLine("已經有人請客,客戶2不用付錢!"); 26 }); 27 t2.IsBackground = true; 28 t2.Start(); 29 30 Pay();//發送通知 31 32 Console.ReadKey(); 33 } 34 35 static void Pay() 36 { 37 string tip = Console.ReadLine(); 38 if (tip == "next") 39 { 40 manualResetEvent.Set();//收銀員發送付錢通知,通過調用Set來通知客戶付錢 41 } 42 } 43 } 復制代碼 運行後屏幕打印: 客戶1在排隊等待付錢... 客戶1在排隊等待付錢... next 已經有人請客,客戶1不用付錢! 已經有人請客,客戶2不用付錢! 如果收銀員在發送通知後5秒就下班了,就不能再收錢了,這時就要把通知重置掉,讓沒接到通知的客戶繼續處於繼續等待,代碼如下: 復制代碼 1 class Program 2 { 3 //若要將初始狀態設置為終止,則為 true;若要將初始狀態設置為非終止,則為 false。 4 static ManualResetEvent manualResetEvent = new ManualResetEvent(false); 5 6 static void Main(string[] args) 7 { 8 Thread t1 = new Thread(() => 9 { 10 Console.WriteLine("客戶1在排隊等待付錢..."); 11 12 //客戶1調用manualResetEvent上的WaitOne來等待付錢通知 13 manualResetEvent.WaitOne(); 14 Console.WriteLine("已經有人請客,客戶1不用付錢"); 15 }); 16 t1.IsBackground = true; 17 t1.Start(); 18 19 Thread t2 = new Thread(() => 20 { 21 Console.WriteLine("客戶2在排隊等待付錢..."); 22 23 Thread.Sleep(8000);//客戶2發呆了8秒,這時收銀員已經下班,要繼續等待 24 //客戶2調用manualResetEvent上的WaitOne來等待付錢通知 25 manualResetEvent.WaitOne(); 26 Console.WriteLine("已經有人請客,客戶2不用付錢!"); 27 }); 28 t2.IsBackground = true; 29 t2.Start(); 30 31 Pay();//發送通知 32 33 Console.ReadKey(); 34 } 35 36 static void Pay() 37 { 38 string tip = Console.ReadLine(); 39 if (tip == "next") 40 { 41 manualResetEvent.Set();//收銀員發送付錢通知,通過調用Set來通知客戶付錢 42 43 Timer timer = new Timer(StopPay, null, 0, 5000);//5秒鐘後收銀員下班了,線程要重新等待了 44 } 45 } 46 47 static void StopPay(object s) 48 { 49 manualResetEvent.Reset(); 50 Console.WriteLine("收銀員下班, 後面的客戶要繼續等待"); 51 } 52 } 復制代碼 運行後屏幕打印: 客戶1在排隊等待付錢... 客戶1在排隊等待付錢... next 已經有人請客,客戶1不用付錢! 收銀員下班,後面的客戶要繼續等待 總結 AutoResetEvent和ManualResetEvent的主要區別就在於:AutoResetEvent一次只能通知一個等待線程,通知後自動關閉; 而ManualResetEvent一次可通知很多個等待的線程,但要關閉需要調用Reset方法手動關閉。