C# 線程間互相通訊 AutoResetEvent和ManualResetEvent

C#線程間互相通訊主要用到兩個類:AutoResetEvent和ManualResetEvent。post

1、AutoResetEvent

AutoResetEvent 容許線程經過發信號互相通訊,線程經過調用 AutoResetEvent 上的 WaitOne 來等待信號。 若是 AutoResetEvent 爲非終止狀態,則線程會被阻止,並等待當前控制資源的線程經過調用 Set 來通知資源可用。ui

下面我用吃快餐的例子來講明這個問題,吃快餐的時候都會排隊付款,收銀員發送收款通知,客戶依次付錢,代碼以下: spa

 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()),屏幕打印:code

客戶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  }

運行後屏幕打印:blog

客戶1在排隊等待付錢...
客戶1在排隊等待付錢...

next
通知來了,客戶1付錢! 

這就說明在通知完客戶1後,autoResetEvent 的狀態又被置爲了false,這時若是要通知到客戶2,就須要在通知完客戶1後,再執行一次通知,在線程1中加上一行代碼,以下: ip

 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,線程纔會所有繼續。input

 

、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方法手動關閉。
相關文章
相關標籤/搜索