C#多线程处理之AutoResetEvent和ManualResetEvent
有时我们在具体开发中,需要使把程序设计成多线程的逻辑。
我们模拟这样一个场景:(C/S模式)
1.客户端向服务端发送测量命令。
2.服务端接受来自客户端的测量命令。
3.服务端进行测量工作。
4.服务端将测量结果,返回给客户端。
以上4步是一个完整的交互过程。当我们要求服务端同时相应有多个客户端的测试命令时,就需要使用到多线程设计:针对每一个客户端建立一个线程执行以上4步,和其他客户端的线程互不干扰。
多线程情况下,每个线程的执行顺序是并行的。但有时我们需要控制多个线程,使他们按特定的顺序执行,比如:当我们给上面的场景中的客服端加上‘优先级’的话。
这里我们讲下如何使用AutoResetEvent和ManualResetEvent来控制多个线程的执行。
1.简单的,我们创建3个线程,t1,t2,t3,依次打印出每个线程的ID,共打印10次。(现实中可能与不到这样的场景,但是这里用于演示如何控制多线程的)
方案a:AutoResetEvent实现:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication14 { class Program { static AutoResetEvent are1 = new AutoResetEvent(true); static AutoResetEvent are2 = new AutoResetEvent(false); static AutoResetEvent are3 = new AutoResetEvent(false); static void Main(string[] args) { Thread t1 = new Thread(new ThreadStart(RunFun1)); t1.Start(); Thread t2 = new Thread(new ThreadStart(RunFun2)); t2.Start(); Thread t3 = new Thread(new ThreadStart(RunFun3)); t3.Start(); Console.Read(); } static void RunFun1() { for (int i = 0; i < 10; i++) { are1.WaitOne(); Console.WriteLine("The " + (i+1) + " times running:"); Console.WriteLine("Thread t1 ID:" + Thread.CurrentThread.ManagedThreadId); are2.Set(); } } static void RunFun2() { for (int i = 0; i < 10; i++) { are2.WaitOne(); Console.WriteLine("Thread t2 ID:" + Thread.CurrentThread.ManagedThreadId); are3.Set(); } } static void RunFun3() { for (int i = 0; i < 10; i++) { are3.WaitOne(); Console.WriteLine("Thread t3 ID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine(); are1.Set(); } } } }
方案b:ManualResetEvent实现:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication15 { class Program { static ManualResetEvent are1 = new ManualResetEvent(true); static ManualResetEvent are2 = new ManualResetEvent(false); static ManualResetEvent are3 = new ManualResetEvent(false); static void Main(string[] args) { Thread t1 = new Thread(new ThreadStart(RunFun1)); t1.Start(); Thread t2 = new Thread(new ThreadStart(RunFun2)); t2.Start(); Thread t3 = new Thread(new ThreadStart(RunFun3)); t3.Start(); Console.Read(); } static void RunFun1() { for (int i = 0; i < 10; i++) { are1.WaitOne(); Console.WriteLine("The " + (i + 1) + " times running:"); Console.WriteLine("Thread t1 ID:" + Thread.CurrentThread.ManagedThreadId); ResetAll(); are2.Set(); } } static void RunFun2() { for (int i = 0; i < 10; i++) { are2.WaitOne(); Console.WriteLine("Thread t2 ID:" + Thread.CurrentThread.ManagedThreadId); ResetAll(); are3.Set(); } } static void RunFun3() { for (int i = 0; i < 10; i++) { are3.WaitOne(); Console.WriteLine("Thread t3 ID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine(); ResetAll(); are1.Set(); } } static void ResetAll() { are1.Reset(); are2.Reset(); are3.Reset(); } } }
AutoResetEvent与ManualResetEvent的使用方式基本一样,区别在与前者被Set后,只能允许一个线程,且只能通过一次。后者则可以允许多个线程,通过多次,直到手动Reset才不允许线程通过。
上面代码的基本思想就是:为每个线程分配一个属于自己的信号量,每个线程执行前都要请求自己的信号量,同时执行完以后,要释放另外一个线程的信号量(既是:唤醒另外线程)。
AuResetEvent变量每次被Set以后,WaitOne执行后,会自动恢复为非信号状态,不需要调用Reset。但是ManualResetEvent则需要。