C# | 使用AutoResetEvent和ManualResetEvent进行线程同步和通信

使用AutoResetEvent和ManualResetEvent进行线程同步和通信

文章目录

介绍

在多线程编程中,AutoResetEvent 和 ManualResetEvent 是两个常用的同步原语。它们用于线程间的通信和协调,以确保线程按照特定的顺序执行。本篇博客将介绍这两种同步原语的概念、用法和区别。

AutoResetEvent

AutoResetEvent (自动重置事件)是一个同步基元,它允许一个线程等待其他线程在信号状态之前进行等待,用于在线程间提供简单的信号通知机制。它的工作方式是,当一个线程通过调用 WaitOne() 方法等待事件信号时,如果事件处于非终止状态,线程将被阻塞。当另一个线程调用 Set() 方法将事件设置为终止状态时,等待的线程将被唤醒,并且事件将自动重置为非终止状态。

ManualResetEvent

ManualResetEvent (手动重置事件)也是一个同步基元,它与AutoResetEvent类似,也用于在线程间提供信号通知机制。与 AutoResetEvent 不同的是,ManualResetEvent 在设置为终止状态后,会一直保持终止状态,直到调用 Reset() 方法将其重置为非终止状态。另外,它允许所有等待的线程 在同一个信号状态下被唤醒。当一个线程通过调用 WaitOne() 方法等待事件信号时,如果事件处于非终止状态,线程将被阻塞。只有当事件被设置为终止状态时,线程才会被唤醒。

异同点

虽然 AutoResetEvent 和 ManualResetEvent 都用于线程间的同步和通信,它们之间有以下几个关键的异同点:

  • 重置行为 :AutoResetEvent 在一个等待线程被唤醒后会自动将事件重置为非终止状态,而 ManualResetEvent 则需要显式地调用 Reset() 方法将事件重置为非终止状态。
  • 信号通知:AutoResetEvent 只允许一个等待线程被唤醒,即使有多个线程等待;而 ManualResetEvent 允许多个等待线程被唤醒。
  • 等待过程:AutoResetEvent 在一个等待线程被唤醒后,其他等待线程仍然会继续等待;而 ManualResetEvent 在一个等待线程被唤醒后,所有等待线程都会被唤醒。

使用场景和代码示例

根据上述的异同点,我们可以根据不同的需求来选择使用 AutoResetEvent 或 ManualResetEvent。

AutoResetEvent 使用示例

我们创建了两个工作线程,并使用 AutoResetEvent 来同步它们的执行。在主线程中,我们先唤醒第一个等待线程,然后等待一段时间再唤醒第二个等待线程。这样,每个线程只会被唤醒一次,然后自动重置事件,继续等待下一个信号。

csharp 复制代码
  using System;
  using System.Threading;

  class Program
  {
      static AutoResetEvent autoResetEvent = new AutoResetEvent(false);

      static void Main(string[] args)
      {
          Thread thread1 = new Thread(Worker);
          Thread thread2 = new Thread(Worker);

          thread1.Start();
          thread2.Start();

          // 唤醒第一个等待线程
          autoResetEvent.Set();

          // 唤醒第二个等待线程
          Thread.Sleep(1000);
          autoResetEvent.Set();

          // 等待线程执行完毕
          thread1.Join();
          thread2.Join();
      }

      static void Worker()
      {
          Console.WriteLine("Worker started");
          autoResetEvent.WaitOne();
          Console.WriteLine("Worker finished");
      }
  }

ManualResetEvent 使用示例

我们同样创建了两个工作线程,但这次使用ManualResetEvent 来同步它们的执行。在主线程中,我们设置了事件为终止状态,这将唤醒所有等待线程。由于 ManualResetEvent 保持终止状态,每个线程只会被唤醒一次,然后继续执行直到结束。

csharp 复制代码
  using System;
  using System.Threading;

  class Program
  {
      static ManualResetEvent manualResetEvent = newManualResetEvent(true);

      static void Main(string[] args)
      {
          Thread thread1 = new Thread(Worker);
          Thread thread2 = new Thread(Worker);

          thread1.Start();
          thread2.Start();

          // 唤醒所有等待线程
          manualResetEvent.Set();

          // 等待线程执行完毕
          thread1.Join();
          thread2.Join();
      }

      static void Worker()
      {
          Console.WriteLine("Worker started");
          manualResetEvent.WaitOne();
          Console.WriteLine("Worker finished");
      }
  }

阻塞多个线程并同时激活

如果需要阻塞多个线程并同时激活多个线程,建议使用 ManualResetEvent。原因是 ManualResetEvent 允许多个等待线程被唤醒,而 AutoResetEvent 只允许一个等待线程被唤醒。

下面是一个使用 ManualResetEvent 的示例代码:

csharp 复制代码
using System;
using System.Threading;

class Program
{
    static ManualResetEvent manualResetEvent = new ManualResetEvent(false);

    static void Main(string[] args)
    {
        Thread[] threads = new Thread[5];

        for (int i = 0; i < threads.Length; i++)
        {
            threads[i] = new Thread(Worker);
            threads[i].Start();
        }

        // 阻塞所有线程
        Console.WriteLine("Blocking all threads...");
        manualResetEvent.WaitOne();

        // 激活所有线程
        Console.WriteLine("Activating all threads...");
        manualResetEvent.Set();

        // 等待线程执行完毕
        foreach (Thread thread in threads)
        {
            thread.Join();
        }
    }

    static void Worker()
    {
        Console.WriteLine("Worker started");
        manualResetEvent.WaitOne();
        Console.WriteLine("Worker finished");
    }
}

在示例中创建了 5 个工作线程,并使用 ManualResetEvent 来阻塞和激活这些线程。执行的流程为:

  1. 主线程将 ManualResetEvent 设置为非终止状态,阻塞所有的工作线程;
  2. 主线程打印消息并将 ManualResetEvent 设置为终止状态,激活所有的工作线程;
  3. 等待所有线程执行完毕。
相关推荐
hz567892 小时前
手术室视频示教系统解决方案:让临床教学突破空间限制
安全·音视频·实时音视频·信息与通信·智能硬件
PHP实战开发录3 小时前
MySQL连接数为什么会打满
系统架构·php·开发
AISenz3 小时前
基于 AIMesh™ 的储气库高频采集与业务控制方案
物联网·信息与通信·工业物联网·物联网协议
混凝土拌意大利面3 小时前
软件架构之职责边界风格(RB)
系统架构
AISenz4 小时前
基于 AIMesh™ 确定性工业无线的 AI 数据中心温控与液冷监测方案
物联网·信息与通信·工业物联网·物联网协议
tang_04274 小时前
【Hi.Ltd 专题】第7期:Threading 采集线程、锁与 LRU 缓存
经验分享·c#·hi.ltd系列
A_nanda4 小时前
C# 界面卡顿:从定位到根治
c#
梁辰兴5 小时前
软件工程:McCall软件质量因素
软件工程·梁辰兴·mccall软件质量因素·mccall模型·产品运行·产品修正·产品转移
2501_930707785 小时前
使用C#代码为新创建的 Word 文档创建目录
c#·word
智慧物业老杨6 小时前
人机协同的物业服务重构:技术落地路径与系统化思考
java·大数据·人工智能·重构·系统架构