文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php教程>C#ManualResetEvent类详解(概念、基本用法、示例、和AutoResetEvent的区别)

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

时间:2025-06-13  来源:互联网  标签: PHP教程

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

一、ManualResetEvent 的概念

  • 定义

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

  • 核心特性

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

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

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

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

  • 应用场景

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

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

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

    二、ManualResetEvent 的基本用法

  • 构造函数

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

    publicManualResetEvent(boolinitialState);i

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

  • 主要方法

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

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

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

  • 示例代码

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

    usingSystem;
    usingSystem.Threading;
    classProgram
    {
    staticManualResetEventmanualEvent=newManualResetEvent(false);
    staticvoidMain()
    {
    ThreadworkerThread=newThread(WorkerMethod);
    workerThread.Start();
    Console.WriteLine("主线程正在等待...");
    Thread.Sleep(2000);//模拟一些工作
    Console.WriteLine("主线程发送信号...");
    manualEvent.Set();//发送信号
    workerThread.Join();//等待子线程结束
    Console.WriteLine("程序结束");
    }
    staticvoidWorkerMethod()
    {
    Console.WriteLine("子线程开始等待...");
    manualEvent.WaitOne();//等待信号
    Console.WriteLine("子线程收到信号并继续执行...");
    }
    }

    输出结果:

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

    三、ManualResetEvent 的实际应用

  • 生产者-消费者模式

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

    usingSystem;
    usingSystem.Collections.Generic;
    usingSystem.Threading;
    classProgram
    {
    staticManualResetEventdataReady=newManualResetEvent(false);
    staticList<int>sharedData=newList<int>();
    staticvoidMain()
    {
    Threadproducer=newThread(Produce);
    Threadconsumer=newThread(Consume);
    producer.Start();
    consumer.Start();
    producer.Join();
    consumer.Join();
    Console.WriteLine("程序结束");
    }
    staticvoidProduce()
    {
    for(inti=1;i<=5;i++)
    {
    Thread.Sleep(1000);//模拟生产数据
    sharedData.Add(i);
    Console.WriteLine($"生产者生成数据:{i}");
    dataReady.Set();//发送信号
    dataReady.Reset();//重置事件
    }
    }
    staticvoidConsume()
    {
    while(true)
    {
    dataReady.WaitOne();//等待信号
    if(sharedData.Count>0)
    {
    intdata=sharedData[0];
    sharedData.RemoveAt(0);
    Console.WriteLine($"消费者消费数据:{data}");
    if(data==5)break;//模拟结束条件
    }
    }
    }
    }

    输出结果:

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

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

    usingSystem;
    usingSystem.Threading;
    classProgram
    {
    staticManualResetEventstartSignal=newManualResetEvent(false);
    staticvoidMain()
    {
    Threadthread1=newThread(()=>Task("Task1"));
    Threadthread2=newThread(()=>Task("Task2"));
    thread1.Start();
    thread2.Start();
    Console.WriteLine("主线程准备发送信号...");
    Thread.Sleep(2000);//模拟延迟
    startSignal.Set();//发送信号
    thread1.Join();
    thread2.Join();
    Console.WriteLine("所有任务完成");
    }
    staticvoidTask(stringname)
    {
    Console.WriteLine($"{name}正在等待信号...");
    startSignal.WaitOne();//等待信号
    Console.WriteLine($"{name}收到信号并开始执行...");
    }
    }

    输出结果:

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

    四、ManualResetEvent 和 AutoResetEvent 的区别

  • 重置方式

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

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

  • 使用场景

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

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

  • 示例对比

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

    usingSystem;
    usingSystem.Threading;
    classProgram
    {
    staticManualResetEventmanualEvent=newManualResetEvent(false);
    staticAutoResetEventautoEvent=newAutoResetEvent(false);
    staticvoidMain()
    {
    Threadt1=newThread(()=>WaitAndPrint("ManualResetEvent",manualEvent));
    Threadt2=newThread(()=>WaitAndPrint("AutoResetEvent",autoEvent));
    t1.Start();
    t2.Start();
    Console.WriteLine("主线程发送信号...");
    manualEvent.Set();//手动重置
    autoEvent.Set();//自动重置
    Thread.Sleep(1000);
    Console.WriteLine("主线程再次发送信号...");
    manualEvent.Set();//再次手动重置
    autoEvent.Set();//再次自动重置
    }
    staticvoidWaitAndPrint(stringtype,EventWaitHandleevt)
    {
    evt.WaitOne();
    Console.WriteLine($"{type}线程被唤醒...");
    }
    }

    输出结果:

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

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

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

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

    以上就是php小编整理的全部内容,希望对您有所帮助,更多相关资料请查看php教程栏目。

    相关阅读更多 +
    最近更新
    排行榜 更多 +
    元梦之星最新版手游

    元梦之星最新版手游

    棋牌卡牌 下载
    我自为道安卓版

    我自为道安卓版

    角色扮演 下载
    一剑斩仙

    一剑斩仙

    角色扮演 下载