当前位置: 首页 > news >正文

C# AutoResetEvent 详解

一、简介

AutoResetEvent 是 .NET 中一个重要的线程同步原语,用于线程间的信号通知。下面我将从多个方面详细讲解 AutoResetEvent。

AutoResetEvent 是 System.Threading 命名空间下的一个类,它表示一个线程同步事件,在等待线程被释放后会自动重置。

核心特点

  • 自动重置:当事件被设置(Set)后,它会自动释放一个等待线程,然后立即重置为非信号状态

  • 线程同步:用于协调多个线程的执行顺序

  • 内核模式:基于操作系统内核对象实现,会有一定的性能开销

二、工作原理

AutoResetEvent 有两种状态:

  1. 有信号状态(Set):允许一个等待线程继续执行

  2. 无信号状态(Reset):使后续线程等待

当调用 Set() 方法时:

  • 如果有线程正在等待,则释放其中一个线程,然后自动重置为无信号状态

  • 如果没有线程等待,则保持有信号状态,直到第一个线程调用 WaitOne()

AutoResetEvent 方法

public class AutoResetEvent : EventWaitHandle
{// 构造函数,initialState参数指定初始状态public AutoResetEvent(bool initialState);// 将事件设置为有信号状态,释放一个等待线程public bool Set();// 将事件设置为无信号状态public bool Reset();// 阻塞当前线程,直到事件变为有信号状态public bool WaitOne();public bool WaitOne(int millisecondsTimeout);public bool WaitOne(TimeSpan timeout);
}

三、基本使用

案例一

C# NetworkStream、ConcurrentDictionary、Socket、SerialPort、广域IP、AutoResetEvent-CSDN博客

    public  class test{public   AutoResetEvent autoResetEvent = new AutoResetEvent(false);  // 初始状态为无信号状态public   void workerThread(){Console.WriteLine("worker thread start ,waiting for  signal  .......    "+ DateTime.Now.ToLongTimeString().ToString());autoResetEvent.WaitOne();    // 等待信号Console.WriteLine("worker thread received  signal ,  continuing work------         " + DateTime.Now.ToLongTimeString().ToString());}}// 调用:static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);//    Application.Run(new Form1());test t=new test();Thread workder = new Thread(t.workerThread);workder.Start();Thread.Sleep(3000);  //休息3sConsole.WriteLine("Main thread signals worker  thread ........     " + DateTime.Now.ToLongTimeString().ToString());t.autoResetEvent.Set();   // 唤醒worker 信号Console.ReadLine();  }

 案例二-两个线程

static   AutoResetEvent itemAvailable = new AutoResetEvent(false);static Queue<int> queue = new Queue<int>();static void Producer(){for (int i = 0; i < 5; i++){Thread.Sleep(5000); // 模拟生产时间lock (queue){queue.Enqueue(i);Console.WriteLine($"Produced item    {i}");}itemAvailable.Set(); // 通知消费者有新项}}static void Consumer(){for (int i = 0; i < 5; i++){itemAvailable.WaitOne(); // 等待新项lock (queue){int item = queue.Dequeue();Console.WriteLine($"Consumed item   {item}");}}}

调用:

Thread producer = new Thread(Producer);Thread consumer = new Thread(Consumer);producer.Start();consumer.Start();Console.ReadLine();  

案例三3个线程

  static  AutoResetEvent autoEvent = new AutoResetEvent(false);static   void Worker(int id){Console.WriteLine($"Worker {id} waiting..." + DateTime.Now.ToLongTimeString().ToString());autoEvent.WaitOne();Console.WriteLine($"Worker {id} released!" + DateTime.Now.ToLongTimeString().ToString());}

调用:

         // 启动3个工作线程for (int i = 1; i <= 3; i++){int id = i;new Thread(() => Worker(id)).Start();}Thread.Sleep(2000);Console.WriteLine("Releasing 1 worker..." + DateTime.Now.ToLongTimeString().ToString());autoEvent.Set(); // 只会释放一个线程Thread.Sleep(3000);Console.WriteLine("Releasing 2 worker..." + DateTime.Now.ToLongTimeString().ToString());autoEvent.Set(); // 释放第二个线程Thread.Sleep(3000);Console.WriteLine("Releasing 3  worker..." + DateTime.Now.ToLongTimeString().ToString());autoEvent.Set(); // 释放第三个线程Console.ReadLine();  

四、多线程使用

说明:

当启动多个线程后,如果需要使用 AutoResetEvent 来精确控制其中某个特定线程的执行,可以采用以下几种方法:

方法一:为每个线程创建单独的 AutoResetEvent

 // 为三个线程分别创建 AutoResetEvent
static   AutoResetEvent event1 = new AutoResetEvent(false);
static   AutoResetEvent event2 = new AutoResetEvent(false);
static   AutoResetEvent event3 = new AutoResetEvent(false);static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);// 启动三个线程,每个线程使用自己的     AutoResetEventThread thread1 = new Thread(() => WorkerTest(1, event1));Thread thread2 = new Thread(() => WorkerTest(2, event2));Thread thread3 = new Thread(() => WorkerTest(3, event3));thread1.Start();thread2.Start();thread3.Start();// 只让第二个线程继续Thread.Sleep(3000);Console.WriteLine("只唤醒第二个线程" + DateTime.Now.ToLongTimeString().ToString());event2.Set();   // 只设置第二个线程的事件Console.ReadLine();}
static  void WorkerTest(int id, AutoResetEvent signal){Console.WriteLine($"Worker {id} 开始等待..." + DateTime.Now.ToLongTimeString().ToString());signal.WaitOne();Console.WriteLine($"Worker {id} 收到信号继续执行!" + DateTime.Now.ToLongTimeString().ToString());}

 

方法二:使用共享 AutoResetEvent 结合标识变量

一个AutoResetEvent  控制3个线程

  // 为三个线程分别创建 AutoResetEventstatic AutoResetEvent eventtest = new AutoResetEvent(false);static  int AutoResetEventId = 0;static object lockobj=new object();/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);// 启动三个线程Thread thread1 = new Thread(() => WorkPlace(1));Thread thread2 = new Thread(() => WorkPlace(2));Thread thread3 = new Thread(() => WorkPlace(3));thread1.Start();thread2.Start();thread3.Start();// 只让第二个线程继续Thread.Sleep(2000);Console.WriteLine("准备唤醒第二个线程" + DateTime.Now.ToLongTimeString().ToString());lock (lockobj){AutoResetEventId = 2;  // 设置允许线程2通过eventtest.Set();    // 发送信号}Console.ReadLine();}static  void WorkPlace(int id){Console.WriteLine($"WorkPlace    {id}  "+DateTime.Now.ToLongTimeString().ToString());// 循环等待直到本线程的信号拿到while (true){eventtest.WaitOne();lock (lockobj){if (AutoResetEventId==id){Console.WriteLine($"WorkPlace    {id}    收到启动信号 " + DateTime.Now.ToLongTimeString().ToString());AutoResetEventId = 0;break;}else{// 如果不是本线程的信号,怎不需要处理eventtest.Set();    }}}}

方法三:使用 Dictionary 管理线程和事件

// 为三个线程分别创建 AutoResetEventstatic   Dictionary<int, AutoResetEvent> threadEvents = new Dictionary<int, AutoResetEvent>();static object dictLock = new object();/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);// 启动三个线程new Thread(() => Worker2(1)).Start();new Thread(() => Worker2(2)).Start();new Thread(() => Worker2(3)).Start();// 等待所有线程注册完成Thread.Sleep(1000);// 只唤醒第二个线程lock (dictLock){if (threadEvents.TryGetValue(2, out var eventToSet)){Console.WriteLine("唤醒第二个线程" + DateTime.Now.ToLongTimeString().ToString());eventToSet.Set();}}Console.ReadLine();}static   void Worker2(int id){AutoResetEvent myEvent;lock (dictLock){myEvent = new AutoResetEvent(false);threadEvents[id] = myEvent;}Console.WriteLine($"Worker2 {id} 开始等待..." + DateTime.Now.ToLongTimeString().ToString());myEvent.WaitOne();Console.WriteLine($"Worker2 {id} 收到信号继续执行!" + DateTime.Now.ToLongTimeString().ToString());}

方法四:使用 ManualResetEvent 和条件判断 

如果需要更灵活的控制,可以结合 ManualResetEvent 和条件判断。

static  ManualResetEvent manualEvent = new ManualResetEvent(false);static   int targetThreadId = 0;static   void Worker3(int id){Console.WriteLine($"Worker {id} 开始等待..." + DateTime.Now.ToLongTimeString().ToString());while (true){manualEvent.WaitOne();if (targetThreadId == id){Console.WriteLine($"Worker {id} 收到信号继续执行!" + DateTime.Now.ToLongTimeString().ToString());manualEvent.Reset();  // 重置事件break;}}}/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);// 启动三个线程new Thread(() => Worker3(1)).Start();new Thread(() => Worker3(2)).Start();new Thread(() => Worker3(3)).Start();// 只让第二个线程继续Thread.Sleep(1000);Console.WriteLine("准备唤醒第二个线程");targetThreadId = 2;manualEvent.Set();  // 设置事件为有信号状态Console.ReadLine();}

 

最佳实践建议

  1. 优先使用方法一:为每个线程分配单独的 AutoResetEvent 是最清晰、最可靠的方式

  2. 避免共享事件:共享 AutoResetEvent 会增加复杂性,容易引入竞态条件

  3. 考虑使用更高级同步原语:如 Barrier、Semaphore 或 Monitor 可能更适合复杂场景

  4. 确保资源释放:记得在使用完毕后调用 Dispose() 释放 AutoResetEvent 资源

性能考虑

  • 每个 AutoResetEvent 都是一个内核对象,创建过多会影响性能

  • 对于高频同步场景,考虑使用用户模式的同步机制如 SpinWait

  • 在 .NET 4.0+ 中,Task 和 async/await 模式可能是更好的选择

 

五、 与 ManualResetEvent 的区别

相关文章:

  • HTTP:十一.HTTP认证概述
  • 内存管理(Linux程序设计)
  • 宿主机和容器 ping 不通域名解决方法
  • 51c大模型~合集120
  • 汽车可变转向比系统的全面认识
  • Linux下载与安装
  • Python内置函数---breakpoint()
  • 基于deepseek的模型微调
  • 校园外卖服务系统的设计与实现(代码+数据库+LW)
  • 智能客服开发实战:用ONE-API构建多模态对话系统
  • 第1节:Backtrader到底是个啥?能干嘛?
  • c语言指针3
  • 免费且开源的企业级监控解决方案:Zabbix
  • JEnv-for-Windows​管理JDK版本
  • 如何提升个人解决问题的能力?
  • 【论文精读】Reformer:高效Transformer如何突破长序列处理瓶颈?
  • 本地服务器 Odoo 安装指南,并实现公网访问
  • STM32提高篇: 蓝牙通讯
  • 服务器上部署Nginx的几种方式
  • 位运算知识
  • 快评|对华关税或“大幅下降”,市场压力之下特朗普“急于与中国达成协议”
  • 国防部发布、中国军号及多家央媒官博发祝福海报:人民海军76岁生日快乐
  • 新片|真人版《星际宝贝史迪奇》5月23日与北美同步上映
  • 用8年还原曹操墓鉴定过程,探寻曹操墓新书创作分享会举行
  • 中国旅游日主题月期间,东航将准备超51.9万套特惠机票
  • 人民日报:对科研不端行为加大惩处力度,让造假成本远高于收益