C#中ManualResetEvent 和 ManualResetEventSlim的使用

从 .NET Framework 版本2.0 开始, ManualResetEvent 派生自 EventWaitHandle 类。 在 ManualResetEvent 功能上等效于EventWaitHandle 使用创建的EventResetMode.ManualReset。ManualResetEventSlim用于实现更好的性能 ManualResetEvent。以下介绍.NET(C#)中ManualResetEvent 和 ManualResetEventSlim的使用总结。

1、ManualResetEvent 和 ManualResetEventSlim

ManualResetEvent表示线程同步事件,收到信号时,必须手动重置该事件。 此类不能被继承。ManualResetEventSlim于实现更好的性能 ManualResetEvent ,而不是等待时间预计非常短,事件不会跨越进程边界。 等待事件收到信号期间,ManualResetEventSlim 会短暂使用忙碌旋转。 等待时间较短时,旋转的开销相对于使用等待句柄来进行等待的开销会少很多。 不过,如果在特定时间段内事件没有收到信号,ManualResetEventSlim 会求助于常规的事件句柄等待。从 .NET Framework 版本4.0 开始, System.Threading.ManualResetEventSlim 类是的轻型替代ManualResetEvent

2、ManualResetEvent的使用

参考文档ManualResetEvent 类 (System.Threading) | Microsoft Learn

new ManualResetEvent(false)为创建在无信号状态。WaitOne()方法会阻塞,调用Set()方法设置为有信号状态,WaitOne()方法不会阻塞,需要调用Reset()方法变为无信号状态,WaitOne()方法将会阻塞。new ManualResetEvent(true)为创建在有信号状态。WaitOne()方法不会阻塞,需要调用Reset()方法变为无信号状态,WaitOne()方法将会阻塞。

例如,

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
         mre用于手动阻塞和释放线程。false是创建在无信号状态。
        private static ManualResetEvent mre = new ManualResetEvent(false);
        static void Main()
        {
            Console.WriteLine("启动 3个在ManualResetEvent上阻塞的命名线程");
            for (int i = 0; i <= 2; i++)
            {
                Thread t = new Thread(ThreadProc);
                t.Name = "Thread_" + i;
                t.Start();
            }
            Thread.Sleep(500);
            Console.WriteLine("按回车键,当三个线程都启动后,调用Set()释放所有线程。");
            Console.ReadLine();
            mre.Set();
            Thread.Sleep(500);
            Console.WriteLine("按回车键,当ManualResetEvent有信号时,调用WaitOne()的线程不会阻塞。");
            Console.ReadLine();
            for (int i = 3; i <= 4; i++)
            {
                Thread t = new Thread(ThreadProc);
                t.Name = "Thread_" + i;
                t.Start();
            }
            Thread.Sleep(500);
            Console.WriteLine("按回车键,调用Reset(),这样线程在调用WaitOne()时就会再次阻塞。");
            Console.ReadLine();
            mre.Reset();
            // Start a thread that waits on the ManualResetEvent.
            Thread t5 = new Thread(ThreadProc);
            t5.Name = "Thread_5";
            t5.Start();
            Thread.Sleep(500);
            Console.WriteLine("按回车键,调用Set()并结束运行。");
            Console.ReadLine();
            mre.Set();
            // 如在Visual Studio中运行这个例子,取消下面这行注释:
            //Console.ReadLine();
        }
        private static void ThreadProc()
        {
            string name = Thread.CurrentThread.Name;
            Console.WriteLine(name + " 调用 mre.WaitOne()");
            mre.WaitOne();
            Console.WriteLine(name + " 结束");
        }
    }
}

3、ManualResetEventSlim的使用

参考文档ManualResetEventSlim 类 (System.Threading) | Microsoft Learn

new ManualResetEventSlim(false)为创建在无信号状态。Wait()方法会阻塞,调用Set()方法设置为有信号状态,Wait()方法不会阻塞,需要调用Reset()方法变为无信号状态,Wait()方法将会阻塞。new ManualResetEventSlim(true)为创建在有信号状态。Wait()方法不会阻塞,需要调用Reset()方法变为无信号状态,Wait()方法将会阻塞。

例如,

using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{
    class Program
    {
        static void Main()
        {
            MRES_SetWaitReset();
            MRES_SpinCountWaitHandle();
        }
        // 
        //      ManualResetEventSlim construction
        //      ManualResetEventSlim.Wait()
        //      ManualResetEventSlim.Set()
        //      ManualResetEventSlim.Reset()
        //      ManualResetEventSlim.IsSet
        static void MRES_SetWaitReset()
        {
            ManualResetEventSlim mres1 = new ManualResetEventSlim(false); // 初始创建为无信号状态
            ManualResetEventSlim mres2 = new ManualResetEventSlim(false); // 初始创建为无信号状态
            ManualResetEventSlim mres3 = new ManualResetEventSlim(true);  // 初始创建为有信号状态
                                                                          // 启动一个操作mres3和mres2的异步任务
            var observer = Task.Factory.StartNew(() =>
            {
                mres1.Wait();
                Console.WriteLine("observer mres1 信号");
                Console.WriteLine("observer Reset mres3 信号");
                mres3.Reset(); // mres3 应该无信号
                Console.WriteLine("observer Set mres2");
                mres2.Set();
            });
 
            Console.WriteLine("主线程: mres3.IsSet = {0} (应该为true)", mres3.IsSet);
            Console.WriteLine("主线程 Set mres1");
            mres1.Set(); // 将会执行 observer Task
            mres2.Wait(); // observer Task完成Reset mres3,Set mres2
            Console.WriteLine("主线程 mres2 有信号");
            Console.WriteLine("主线程: mres3.IsSet = {0} (应该为false)", mres3.IsSet);
 
            // 用完ManualResetEventSlim时,Dispose()一个ManualResetEventSlim释放资源
            observer.Wait(); // 确保observer执行完成
            mres1.Dispose();
            mres2.Dispose();
            mres3.Dispose();
            // 如在Visual Studio中运行这个例子,取消下面这行注释:
            Console.ReadLine();
        }
 
        // :
        //      ManualResetEventSlim construction w/ SpinCount
        //      ManualResetEventSlim.WaitHandle
        static void MRES_SpinCountWaitHandle()
        {
            // 构造一个SpinCount为1000的ManualResetEventSlim
            ManualResetEventSlim mres1 = new ManualResetEventSlim(false, 1000);
            ManualResetEventSlim mres2 = new ManualResetEventSlim(false, 1000);
 
            Task bgTask = Task.Factory.StartNew(() =>
            {
                // Just wait a little
                Thread.Sleep(100);
 
                // 给两个MRESes发信号
                Console.WriteLine("mres1.Set() mres2.Set()");
                mres1.Set();
                mres2.Set();
            });
            //等待指定数组中的所有元素都收到信号。
            WaitHandle.WaitAll(new WaitHandle[] { mres1.WaitHandle, mres2.WaitHandle });
            Console.WriteLine("WaitHandle.WaitAll(mres1.WaitHandle, mres2.WaitHandle) completed.");
 
            // Clean up
            bgTask.Wait();
            mres1.Dispose();
            mres2.Dispose();
        }
    }
}
相关推荐
Am心若依旧40918 分钟前
[c++11(二)]Lambda表达式和Function包装器及bind函数
开发语言·c++
明月看潮生20 分钟前
青少年编程与数学 02-004 Go语言Web编程 20课题、单元测试
开发语言·青少年编程·单元测试·编程与数学·goweb
大G哥30 分钟前
java提高正则处理效率
java·开发语言
VBA633740 分钟前
VBA技术资料MF243:利用第三方软件复制PDF数据到EXCEL
开发语言
轩辰~42 分钟前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
小_太_阳1 小时前
Scala_【1】概述
开发语言·后端·scala·intellij-idea
向宇it1 小时前
【从零开始入门unity游戏开发之——unity篇02】unity6基础入门——软件下载安装、Unity Hub配置、安装unity编辑器、许可证管理
开发语言·unity·c#·编辑器·游戏引擎
yngsqq1 小时前
一键打断线(根据相交点打断)——CAD c# 二次开发
windows·microsoft·c#
古希腊掌管学习的神1 小时前
[LeetCode-Python版]相向双指针——611. 有效三角形的个数
开发语言·python·leetcode
赵钰老师1 小时前
【R语言遥感技术】“R+遥感”的水环境综合评价方法
开发语言·数据分析·r语言