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

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

image.png

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

@[toc]

介绍

在多线程编程中,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 来同步它们的执行。在主线程中,我们先唤醒第一个等待线程,然后等待一段时间再唤醒第二个等待线程。这样,每个线程只会被唤醒一次,然后自动重置事件,继续等待下一个信号。

  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 保持终止状态,每个线程只会被唤醒一次,然后继续执行直到结束。

  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 的示例代码:

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. 等待所有线程执行完毕。
相关文章
|
4月前
|
数据采集 XML JavaScript
C# 中 ScrapySharp 的多线程下载策略
C# 中 ScrapySharp 的多线程下载策略
|
2月前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
41 1
[Java]线程生命周期与线程通信
|
5月前
|
Java
实现Java多线程中的线程间通信
实现Java多线程中的线程间通信
|
1月前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
38 3
|
2月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
24 1
|
2月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
48 1
|
2月前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
31 1
|
2月前
|
Java
|
2月前
多线程通信和同步的方式有哪些?
【10月更文挑战第6天】
118 0