一、基础概念
在多任务环境下,线程等待是非常重要的一个概念。线程等待可以让一个线程等待另外一个线程完成某些操作,然后再继续执行。在C#中,线程等待是通过System.Threading命名空间中的WaitHandle类和其派生类来实现的。
WaitHandle类提供WaitOne()和WaitAny()方法,它们都可以让一个线程等待某个事件,直到该事件发生或超时。WaitOne()方法会等待一个WaitHandle对象的信号,并且支持超时;WaitAny()方法会等待多个WaitHandle对象中的任意一个被信号激活,并且支持超时。
在具体的应用场景中,我们可以使用ManualResetEvent、AutoResetEvent、CountdownEvent和SemaphoreSlim等类来创建WaitHandle对象。
二、ManualResetEvent和AutoResetEvent
ManualResetEvent和AutoResetEvent都是WaitHandle的派生类,它们可以用于线程等待。在 ManualResetEvent 中,线程会一直等待直到该事件被信号激活。而在 AutoResetEvent 中,线程会等待事件被信号激活一次,然后自动重置,以便于下次被信号激活。
下面是使用ManualResetEvent实现线程等待的代码示例:
ManualResetEvent event1 = new ManualResetEvent(false); ThreadPool.QueueUserWorkItem(state => { Console.WriteLine("Thread 1 is running"); Thread.Sleep(1000); Console.WriteLine("Thread 1 set the event"); event1.Set(); }); Console.WriteLine("Main thread is waiting for event"); event1.WaitOne(); Console.WriteLine("Main thread received event");
上述示例使用ManualResetEvent实现了一个线程等待的功能。在主线程中,首先创建了一个ManualResetEvent对象,并且初始化为未激活状态。然后开启一个新的线程,在该线程中等待1秒钟,并且在1秒钟后激活ManualResetEvent对象。在主线程中调用event1.WaitOne()方法等待事件的激活,直到该事件被激活后,主线程才会继续执行。
三、CountdownEvent和SemaphoreSlim
CountdownEvent和SemaphoreSlim也是WaitHandle的派生类。CountdownEvent可以用来等待一组操作完成,而SemaphoreSlim可以用来控制同时执行的线程数量。
下面是使用CountdownEvent实现线程等待的代码示例:
CountdownEvent countdown = new CountdownEvent(3); for (int i = 1; i <= 3; i++) { ThreadPool.QueueUserWorkItem(state => { Console.WriteLine($"Thread {i} is running"); Thread.Sleep(1000); Console.WriteLine($"Thread {i} is done"); countdown.Signal(); }); } countdown.Wait(); Console.WriteLine("All threads are done");
上述示例使用CountdownEvent实现了同时等待多个线程完成的功能。首先创建了一个CountdownEvent对象,并且把初始计数器设置为3。然后开启三个新的线程,在每个线程中等待1秒钟,并且在1秒钟后为CountdownEvent对象计数。在主线程中调用countdown.Wait()方法等待所有的线程完成,并且在所有线程完成后继续执行。
下面是使用SemaphoreSlim实现控制线程数量的代码示例:
SemaphoreSlim semaphore = new SemaphoreSlim(3); for (int i = 1; i <= 5; i++) { ThreadPool.QueueUserWorkItem(async state => { await semaphore.WaitAsync(); Console.WriteLine($"Thread {i} start"); Thread.Sleep(1000); Console.WriteLine($"Thread {i} end"); semaphore.Release(); }); }
上述示例使用SemaphoreSlim实现了在同一时间内只允许3个线程同时执行的功能。首先创建了一个SemaphoreSlim对象,并且初始计数器设置为3。然后使用ThreadPool开启5个新的线程,在每个线程中等待SemaphoreSlim对象信号,当计数器大于0时,线程可以执行,并且计数器会减1。在线程执行完毕后,计数器会加1,以便于其他线程可以继续执行。