[简介]
常用网名: 猪头三
出生日期: 1981.XX.XX
QQ: 643439947
个人网站: 80x86汇编小站 https://www.x86asm.org
编程生涯: 2001年~至今[共22年]
职业生涯: 20年
开发语言: C/C++、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python
开发工具: Visual Studio、Delphi、XCode、Eclipse、C++ Builder
技能种类: 逆向 驱动 磁盘 文件
研发领域: Windows应用软件安全/Windows系统内核安全/Windows系统磁盘数据安全/macOS应用软件安全
项目经历: 磁盘性能优化/文件系统数据恢复/文件信息采集/敏感文件监测跟踪/网络安全检测
[序言]
为了优化"[原创][3]探究C#多线程开发细节-“用ConcurrentQueue<T>解决多线程的无顺序性的问题“-CSDN博客"文章里面的代码示例, 需要专门为"ManualResetEvent类"写一篇文章. 只有解了"ManualResetEvent类"的作用, 才会知道如何优化.
[为什么要优化"[原创][3]探究C#多线程开发细节-"用ConcurrentQueue<T>解决多线程的无顺序性的问题""文章里面的代码示例?]
如果认真阅读了代码, 就会发现, 在进行多线程同步的时候, 竟然使用了for循环+Sleep()组合来实现轮询. 这是非常友好的行为. 这里有一个弊端:for循环会不断的消耗CPU资源, 虽然通过Sleep(5)这样极短的休眠时间降低CPU的使用率, 但仅仅是给软件使用者造成一种假象. 但实际上, 把时间放大到1秒钟, 这个for循环要执行200次, 给CPU的负担也不小的.
[为了避免for循环+Sleep()这样初级的写法, 可以使用ManualResetEvent类解决.]
那么ManualResetEvent类的作用和效果是如何的呢?可以把ManualResetEvent类看作是一个红绿灯, 当ManualResetEvent类处于红灯状态的时候, 线程执行等待, 不允许执行. 当ManualResetEvent类处于绿灯状态, 线程就恢复执行状态, 继续上一次操作. 好比现实中的红绿灯过马路的效果.下面简单的看看ManualResetEvent类的使用方法
1> ManualResetEvent event_ControlThread = new ManualResetEvent(false); // 创建一个ManualResetEvent类, 并设置false(即红灯状态)
2> event_ControlThread.WaitOne(); // 等待并阻塞. 如果是红灯状态, 该代码就阻塞着, 不给线程执行event_ControlThread.WaitOne()之后的任何代码. 反之, 绿灯状态, 线程可以继续往下执行.
3> event_ControlThread.Reset(); // 等当前状态是处于绿灯状态的时候, 如果恢复为红灯状态, 那就执行一次Reset()操作.
4> event_ControlThread.Set(); // 如果当前状态处于红灯状态, 通过Set()一次, 就会变成绿灯状态, 随后绿灯灭掉. 最终的效果就是灯灭了. 因此为了保持灯处于亮灯状态, 一定要通过Reset()操作复原.
[如果理解上面的所说的, 可以尝试下一个例子]
1> 启动Visual Studio Enterprise 2022版本
2> 建立一个C# Windows窗体应用(.NET Framework).
3> 然后在窗体上放上两个按钮和一个Lable控件
4> 按钮1 : 启动一个带有for循环的线程, 然后这个for循环可以针对变量int_Count进行+1操作
5> 按钮2 : 调用event_ControlThread.Set()处理, 每单击一次, for循环线程才能运行一次.
完成上面的步骤之后, 模仿下面的代码, 抄写到你建立的项目中.
public partial class Form_Event_Demo : Form{private int mpr_int_Count = 0;// private AutoResetEvent event_ControlThread = new AutoResetEvent(false);private ManualResetEvent event_ControlThread = new ManualResetEvent(false); // 创建事件并初始化为红灯状态public Form_Event_Demo(){InitializeComponent();}// 启动循环 +1 线程private void Bn_StartThread_Click(object sender, EventArgs e){Thread class_LoopThread = new Thread(() =>{for (int int_Index = 0; int_Index < 10; int_Index++){event_ControlThread.WaitOne(); // 等待事件信号(即等待亮绿灯)mpr_int_Count++;// 界面显示结果if (lb_Result.InvokeRequired){this.Invoke((MethodInvoker)delegate {lb_Result.Text = $"当前结果: {mpr_int_Count}";});}else{lb_Result.Text = $"当前结果: {mpr_int_Count}";}event_ControlThread.Reset(); // 手动重置信号(即把灭灯状态恢复为红灯状态)}});class_LoopThread.Start();}// End Bn_StartThread_Click()private void Bn_Increment_Click(object sender, EventArgs e){event_ControlThread.Set(); //设置信号(即把红灯切换为绿灯, 然后再灭灯), 通知线程执行一次循环}// End Bn_Increment_Click()}
[代码还可以在优化一下, ManualResetEvent()可以替换为AutoResetEvent(), 这样更加自动化]
AutoResetEvent()的好处就是, 如果灭灯状态, 它会自动帮你恢复为亮灯状态, 而不需要手工去Reset()一次操作.
[结尾]
这次内容看起来很简单, 但是必须要用心体会到它的工作原理和效果, 只有体验到了同步效果, 才会写出更好的多线程并发程序. 大家如果阅读完这篇文章, 有更多疑问可以留言, 有更好的建议和想法,也可以留下你的评论.
[效果演示]
初步体验ManualResetEvent类带来的同步效果