掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务

C#ManualResetEvent类详解(概念、基本用法、示例、和AutoResetEvent的区别)

在多线程编程中,线程间的同步是一个重要且复杂的任务。C# 提供了多种同步机制来解决这个问题,其中 ManualResetEvent 是一个常用的工具类。它允许线程通过信号量来控制执行顺序,从而实现线程间的协作和同步。本文将详细探讨 ManualResetEvent 的概念、基本用法、示例以及它与 AutoResetEvent 的区别。

一、ManualResetEvent 的概念

  1. 定义

ManualResetEvent 是 C# 中用于线程同步的一个类,继承自 EventWaitHandle。它提供了一个手动重置的事件对象,允许线程等待某个条件满足后再继续执行。

  1. 核心特性

手动重置:与其他同步机制不同,ManualResetEvent 的状态(信号或非信号)需要手动设置。

信号状态:当处于“有信号”状态时,所有等待的线程都会被释放并继续执行。

当处于“无信号”状态时,线程会阻塞直到事件变为“有信号”。

持久性:一旦设置为“有信号”,所有等待的线程都会被唤醒,且状态不会自动重置。

  1. 应用场景

控制多个线程的执行顺序。

实现生产者-消费者模式。

等待某些条件完成后再继续执行。

二、ManualResetEvent 的基本用法

  1. 构造函数

ManualResetEvent 提供了一个构造函数,用于初始化事件的状态:

public ManualResetEvent(bool initialState);i

nitialState:表示事件的初始状态。如果为 true,事件处于“有信号”状态;如果为 false,事件处于“无信号”状态。

  1. 主要方法

Set():将事件状态设置为“有信号”,允许所有等待的线程继续执行。

Reset():将事件状态设置为“无信号”,使线程进入阻塞状态。

WaitOne():当前线程阻塞,直到事件变为“有信号”。

  1. 示例代码

以下是一个简单的示例,展示如何使用 ManualResetEvent 来控制线程的执行顺序:

using System;
using System.Threading;
class Program
{
    static ManualResetEvent manualEvent = new ManualResetEvent(false);
    static void Main()
    {
        Thread workerThread = new Thread(WorkerMethod);
        workerThread.Start();
        Console.WriteLine("主线程正在等待...");
        Thread.Sleep(2000); // 模拟一些工作
        Console.WriteLine("主线程发送信号...");
        manualEvent.Set(); // 发送信号
        workerThread.Join(); // 等待子线程结束
        Console.WriteLine("程序结束");
    }
    static void WorkerMethod()
    {
        Console.WriteLine("子线程开始等待...");
        manualEvent.WaitOne(); // 等待信号
        Console.WriteLine("子线程收到信号并继续执行...");
    }
}

输出结果:

子线程开始等待...
主线程正在等待...
主线程发送信号...
子线程收到信号并继续执行...
程序结束

三、ManualResetEvent 的实际应用

  1. 生产者-消费者模式

ManualResetEvent 常用于实现生产者-消费者模式,确保生产者生成数据后消费者才能消费。

using System;
using System.Collections.Generic;
using System.Threading;
class Program
{
    static ManualResetEvent dataReady = new ManualResetEvent(false);
    static List<int> sharedData = new List<int>();
    static void Main()
    {
        Thread producer = new Thread(Produce);
        Thread consumer = new Thread(Consume);
        producer.Start();
        consumer.Start();
        producer.Join();
        consumer.Join();
        Console.WriteLine("程序结束");
    }
    static void Produce()
    {
        for (int i = 1; i <= 5; i++)
        {
            Thread.Sleep(1000); // 模拟生产数据
            sharedData.Add(i);
            Console.WriteLine($"生产者生成数据: {i}");
            dataReady.Set(); // 发送信号
            dataReady.Reset(); // 重置事件
        }
    }
    static void Consume()
    {
        while (true)
        {
            dataReady.WaitOne(); // 等待信号
            if (sharedData.Count > 0)
            {
                int data = sharedData[0];
                sharedData.RemoveAt(0);
                Console.WriteLine($"消费者消费数据: {data}");
                if (data == 5) break; // 模拟结束条件
            }
        }
    }
}

输出结果:

生产者生成数据: 1
消费者消费数据: 1
生产者生成数据: 2
消费者消费数据: 2
生产者生成数据: 3
消费者消费数据: 3
生产者生成数据: 4
消费者消费数据: 4
生产者生成数据: 5
消费者消费数据: 5
程序结束
  1. 线程启动控制

ManualResetEvent 可以用来控制线程的启动时间,确保某些线程在特定条件下才开始执行。

using System;
using System.Threading;
class Program
{
    static ManualResetEvent startSignal = new ManualResetEvent(false);
    static void Main()
    {
        Thread thread1 = new Thread(() => Task("Task 1"));
        Thread thread2 = new Thread(() => Task("Task 2"));
        thread1.Start();
        thread2.Start();
        Console.WriteLine("主线程准备发送信号...");
        Thread.Sleep(2000); // 模拟延迟
        startSignal.Set(); // 发送信号
        thread1.Join();
        thread2.Join();
        Console.WriteLine("所有任务完成");
    }
    static void Task(string name)
    {
        Console.WriteLine($"{name} 正在等待信号...");
        startSignal.WaitOne(); // 等待信号
        Console.WriteLine($"{name} 收到信号并开始执行...");
    }
}

输出结果:

Task 1 正在等待信号...
Task 2 正在等待信号...
主线程准备发送信号...
Task 1 收到信号并开始执行...
Task 2 收到信号并开始执行...
所有任务完成

四、ManualResetEvent 和 AutoResetEvent 的区别

  1. 重置方式

ManualResetEvent:事件状态需要手动调用 Reset() 方法进行重置。一旦设置为“有信号”,所有等待的线程都会被唤醒。

AutoResetEvent:事件状态在释放一个线程后会自动重置为“无信号”。每次只能唤醒一个线程。

  1. 使用场景

ManualResetEvent:适用于需要同时唤醒多个线程的场景。

AutoResetEvent:适用于每次只允许一个线程继续执行的场景。

  1. 示例对比

以下代码展示了两者的差异:

using System;
using System.Threading;
class Program
{
    static ManualResetEvent manualEvent = new ManualResetEvent(false);
    static AutoResetEvent autoEvent = new AutoResetEvent(false);
    static void Main()
    {
        Thread t1 = new Thread(() => WaitAndPrint("ManualResetEvent", manualEvent));
        Thread t2 = new Thread(() => WaitAndPrint("AutoResetEvent", autoEvent));
        t1.Start();
        t2.Start();
        Console.WriteLine("主线程发送信号...");
        manualEvent.Set(); // 手动重置
        autoEvent.Set();   // 自动重置
        Thread.Sleep(1000);
        Console.WriteLine("主线程再次发送信号...");
        manualEvent.Set(); // 再次手动重置
        autoEvent.Set();   // 再次自动重置
    }
    static void WaitAndPrint(string type, EventWaitHandle evt)
    {
        evt.WaitOne();
        Console.WriteLine($"{type} 线程被唤醒...");
    }
}

输出结果:

主线程发送信号...
ManualResetEvent 线程被唤醒...
AutoResetEvent 线程被唤醒...
主线程再次发送信号...
ManualResetEvent 线程被唤醒...
AutoResetEvent 线程被唤醒...

从输出可以看出,ManualResetEvent 在多次发送信号时仍然保持“有信号”状态,而 AutoResetEvent 每次只能唤醒一个线程。

C#ManualResetEvent类详解(概念、基本用法、示例、和AutoResetEvent的区别)

ManualResetEvent 是 C# 中一个强大的线程同步工具,适合需要手动控制事件状态的场景。通过设置和重置事件状态,它可以灵活地控制线程的执行顺序和协作方式。与 AutoResetEvent 相比,ManualResetEvent 更适合需要同时唤醒多个线程的场景,而 AutoResetEvent 则更适合每次只允许一个线程继续执行的情况。掌握这两者的区别和用法,能够帮助开发者更高效地设计和实现多线程应用程序。

声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com

  • 火车订票查询

    通过站到站查询火车班次时刻表等信息,并已经集成至聚合MCP Server。

    通过站到站查询火车班次时刻表等信息,并已经集成至聚合MCP Server。

  • 公安不良查询

    公安七类重点高风险人员查询

    公安七类重点高风险人员查询

  • 车辆过户信息查询

    通过车辆vin码查询车辆的过户次数等相关信息

    通过车辆vin码查询车辆的过户次数等相关信息

  • 银行卡五元素校验

    验证银行卡、身份证、姓名、手机号是否一致并返回账户类型

    验证银行卡、身份证、姓名、手机号是否一致并返回账户类型

  • 高风险人群查询

    查询个人是否存在高风险行为

    查询个人是否存在高风险行为

0512-88869195
数 据 驱 动 未 来
Data Drives The Future