3.可等待事件(EventWaitHandle)
EventWaitHandle派生自WaitHandle,用于跨线程通知事件。
EventWaitHandle有两种方式:手动重置(ManualReset)和自动重置(AutoReset)对应强类型子类ManualResetEvent和AutoResetEvent。
手动重置事件
public class EventDemo : IDisposable
{
//创建手动重置事件对象
private ManualResetEvent m_Event;
//构造函数
public EventDemo()
{
m_Event = new ManualResetEvent(false);
//启动一个新线程
Thread thread = new Thread(DoWork);
thread.Start();
}
public void DoWork()
{
int counter = 0;
while (true)
{
//阻塞当前线程,直到收到信号
m_Event.WaitOne();
counter++;
Console.WriteLine("编号:" + counter);
}
}
public void GoThread()
{
//设置句柄状态为:信号已发
//一旦信号状态为已发,它就停留在已发状态,直到手动调用Reset()
//信号已发,所有等待线程被解锁
m_Event.Set();
Console.WriteLine("开始!");
}
public void StopThread()
{
//设置句柄状态为:信号未发
//阻塞线程
m_Event.Reset();
Console.WriteLine("中断!");
}
#region IDisposable 成员
public void Dispose()
{
m_Event.Close();
}
#endregion
}
//调用
EventDemo ed = new EventDemo();
//在另一线程上调用ed的GoThread()等方法
ed.GoThread();
Thread.Sleep(10);
ed.StopThread();
ed.GoThread();
Thread.Sleep(10);
ed.StopThread();
自动重置事件
和手动重置事件基本相同,区别在:自动重置一旦调用Set()后,处于阻塞的线程开始运行,同时在这一刻,状态自动反转为型号未发(无需调用Rest())。如调用Set(),无线程等待,将继续保持信号已发状态。自动重置事件将Set()和Reset()调用结合到一个原子操作中。
private AutoResetEvent m_Event;
/* 其他同上 */
//调用
//无需调用StopThread(),完成一原子操作状态自动变为未发信号
ed.GoThread();
Thread.Sleep(10);
ed.GoThread();
线程集结问题
线程相互等待,然后同时继续执行(并发完成)。
class Rendezvous
{
//初始化信号为已发送状态(终止状态)
AutoResetEvent m_First = new AutoResetEvent(true);
AutoResetEvent m_Event1 = new AutoResetEvent(false);
AutoResetEvent m_Event2 = new AutoResetEvent(false);
//在某一点,每个线程调用该方法,
//最先调用Wait()的线程,最先被阻塞
//一旦第二个线程调用Wait(),两个线程都自动继续执行任务
public void Wait()
{
//判断线程是否第一个调用
//如果m_First收到信号(信号已发送状态),则为 true;否则为 false;
//由于为AutoResetEvent实例,执行完原子操作后,会自动重置事件状态为信号未发状态,因此第二调用的线程返回值始终为false
bool first = m_First.WaitOne(TimeSpan.Zero, false);
if (first)
{
//给m_Event1发信号并等待m_Event2
//SignalAndWait使发信号和等待为一元子操作进行,保证两线程,互相等待然后同时继续执行
WaitHandle.SignalAndWait(m_Event1,m_Event2);
}
else
{
//给m_Event2发信号并等待m_Event1
WaitHandle.SignalAndWait(m_Event2, m_Event1);
}
}
public void Reset()
{
//重置m_First为信号已发送状态,用于再次集结两个线程
m_First.Set();
}
}
class Program
{
static Rendezvous m_Rend = new Rendezvous();
public static void ThreadMethod1()
{
m_Rend.Wait();
Console.WriteLine(Thread.CurrentThread.Name);
}
public static void ThreadMethod2()
{
m_Rend.Wait();
Console.WriteLine(Thread.CurrentThread.Name );
}
static void Main(string[] args)
{
//调用
Thread threadA = new Thread(ThreadMethod1);
threadA.Name = "threadA";
threadA.Start();
Thread threadB = new Thread(ThreadMethod2);
threadB.Name = "threadB";
threadB.Start();
Console.Read();
}
}