今儿个,咱来聊聊ManualResetEvent这个玩意儿,这东西在多线程编程里头可有点儿用,能帮咱控制线程啥时候跑、啥时候歇着。说白,就是个信号灯。
我也不咋明白这玩意儿到底咋使,就去网上搜搜,看些资料。还真有点儿绕,不过琢磨琢磨,也就那么回事儿。
摸索过程
我一开始是想实现这么一个功能:让一个线程先跑着,跑到一半儿,让它停下来,等另一个线程干完活儿,再让这个线程继续跑。这不就是线程之间的协作嘛
我直接上代码,想看看这ManualResetEvent到底咋回事。我先是这样写的:
ManualResetEvent mre = new ManualResetEvent(false);
这句代码的意思,就是创建一个ManualResetEvent对象,名字叫mre,然后给它一个初始状态,false。这false啥意思?就是说,这个信号灯是红灯,不让过。
然后,我写两个线程,一个线程里头,我让它先跑一段儿,然后调用*(),意思就是让这个线程在这儿等着,等绿灯亮再走。
另一个线程里头,我干点儿别的活儿,干完,调用*(),意思就是把信号灯变成绿灯,让等着的那哥们儿可以走。
我这么一跑,还真行!第一个线程跑到一半儿,停下来,等第二个线程干完活儿,它又继续跑。
实践出真知
光看不练假把式,我得动手试试。我写个小例子,模拟两个线程下载文件。一个线程负责下载文件头,另一个线程负责下载文件体。我想让下载文件头的线程先跑,下载完文件头,然后停下来,等下载文件体的线程下载完文件体,再把它们合并起来。
代码大概是这样的:
// 创建ManualResetEvent对象,初始状态为false,表示红灯
ManualResetEvent mre = new ManualResetEvent(false);

// 下载文件头的线程
Thread thread1 = new Thread(() =>
*("开始下载文件头...");
// 模拟下载文件头
*(2000);

*("文件头下载完成!");
// 等待信号,绿灯亮才能继续
*("开始合并文件...");
// 模拟合并文件
*(1000);

*("文件合并完成!");
// 下载文件体的线程
Thread thread2 = new Thread(() =>
*("开始下载文件体...");

// 模拟下载文件体
*(3000);
*("文件体下载完成!");
// 发送信号,把红灯变绿灯

// 启动线程
我跑一下这个例子,结果跟我预期的一样:
- 先打印“开始下载文件头...”。
- 然后等2秒,打印“文件头下载完成!”。
- 它就停在那儿,等着。
- 再过3秒,打印“开始下载文件体...”和“文件体下载完成!”。
- 打印“开始合并文件...”和“文件合并完成!”。
一点小总结
通过这回实践,我对ManualResetEvent的理解更深。这玩意儿就像一个开关,或者说一个信号灯。new ManualResetEvent(false)
就是创建一个红灯,就是让线程在这儿等着,直到等到绿灯,就是把红灯变绿灯,让之前等待的线程继续。如果一开始是new ManualResetEvent(true)
,那就是绿灯,线程一来就能直接过,不用等。
而且这ManualResetEvent,跟AutoResetEvent还不太一样。ManualResetEvent,你Set()一次,所有等着WaitOne()的线程都能收到信号继续走。但是必须手动调用Reset()重置信号量状态为非终止。而AutoResetEvent,你Set()一次,就只有一个线程能收到,然后它就自动把灯变回红色。这个得根据实际情况来选择用哪个。
这ManualResetEvent还是挺有用的,以后在多线程编程的时候,又多一个工具可以用!