当前位置: 代码网 > it编程>编程语言>Asp.net > C# AutoResetEvent和ManualResetEvent的实现示例

C# AutoResetEvent和ManualResetEvent的实现示例

2025年11月27日 Asp.net 我要评论
autoresetevent 和 manualresetevent 是 .net 中用于线程同步的两个重要类,都继承自 eventwaithandle。它们的核心区别在于 “信号触发后是否

autoresetevent 和 manualresetevent 是 .net 中用于线程同步的两个重要类,都继承自 eventwaithandle。它们的核心区别在于 “信号触发后是否自动重置状态”。

✅ 一句话总结区别:

  • autoresetevent:
    信号触发(set)后,只允许一个等待线程通过,然后自动重置为非信号状态(false)。

  • manualresetevent:
    信号触发(set)后,所有等待线程都可以通过,并且保持信号状态(true),直到你手动调用 reset() 才关闭。

🔍 详细对比

特性autoreseteventmanualresetevent
初始状态可指定(默认 false)可指定(默认 false)
调用 set() 后允许一个等待线程继续执行,然后自动变为 false所有等待线程继续执行,并保持 true
如何关闭信号自动关闭(无需干预)必须显式调用 reset()
类比旋转门(一次只过一人)闸门(打开后所有人都能过,直到手动关上)
典型用途线程间一对一通知、生产者-消费者单次唤醒多线程同时启动/停止、初始化完成广播

💡 代码示例说明

示例 1:autoresetevent—— 一次只唤醒一个线程

var are = new autoresetevent(false);

// 启动3个等待线程
for (int i = 0; i < 3; i++)
{
    int id = i;
    task.run(() =>
    {
        console.writeline($"线程 {id} 等待中...");
        are.waitone(); // 阻塞直到收到信号
        console.writeline($"线程 {id} 被唤醒!");
    });
}

thread.sleep(1000);
console.writeline("主线程发出信号...");

are.set(); // ⚠️ 只会唤醒 one 线程!
thread.sleep(100); 
are.set(); // 再唤醒一个
thread.sleep(100);
are.set(); // 再唤醒最后一个

输出(顺序可能不同)

线程 0 等待中...
线程 1 等待中...
线程 2 等待中...
主线程发出信号...
线程 1 被唤醒!
线程 0 被唤醒!
线程 2 被唤醒!

✅ 每次 set() 只放行一个线程,之后自动关闭。

示例 2:manualresetevent—— 一次性唤醒所有线程

var mre = new manualresetevent(false);

for (int i = 0; i < 3; i++)
{
    int id = i;
    task.run(() =>
    {
        console.writeline($"线程 {id} 等待中...");
        mre.waitone();
        console.writeline($"线程 {id} 被唤醒!");
    });
}

thread.sleep(1000);
console.writeline("主线程发出信号...");
mre.set(); // 🔓 所有等待线程立即通过!

// 不需要多次 set,也不自动关闭

输出

线程 0 等待中...
线程 1 等待中...
线程 2 等待中...
主线程发出信号...
线程 2 被唤醒!
线程 0 被唤醒!
线程 1 被唤醒!

✅ 一次 set(),全部放行!
❗ 如果之后再有线程调用 waitone(),也会立即通过(因为信号仍为 true),除非你调用 mre.reset()。

🛠 常见使用场景

✅autoresetevent适用场景:

  • 生产者-消费者模型:每次生产一个数据,唤醒一个消费者。
  • 线程池任务分发:一个任务就绪,通知一个工作线程处理。
  • 精确控制单次唤醒

✅manualresetevent适用场景:

  • 程序初始化完成广播:等所有资源加载完,通知所有线程开始工作。
  • 并行测试启动:让多个线程同时开始执行(避免先后差异)。
  • 全局暂停/恢复控制:如游戏暂停系统。
// 初始化完成示例
var initcomplete = new manualresetevent(false);

task.run(() => {
    loadconfig();
    loaddatabase();
    initcomplete.set(); // 所有准备工作完成
});

// 多个工作线程等待初始化完成
task.run(() => {
    initcomplete.waitone(); // 等待初始化
    dowork();
});

⚠️ 注意事项

  1. 性能:两者都是内核对象(kernel object),涉及用户态 ↔ 内核态切换,频繁使用有开销。高频场景可考虑 spinwaitmonitor
  2. 替代方案(.net core / .net 5+)
    • 使用 taskcompletionsource<t> 实现异步通知(更现代)
    • 使用 countdowneventbarrier 等高级同步原语
  3. 不要混淆 reset() 行为
    • autoreseteventreset() 是多余的(它自己会重置)
    • manualresetevent 必须手动 reset() 才能再次阻塞线程

✅ 总结记忆口诀:

  • auto自动关门(过一人,门自动关)
  • manual手动关门(门开了,所有人能过,得你手动关)

选择哪个?
👉 需要逐个唤醒?用 autoresetevent
👉 需要集体唤醒?用 manualresetevent

到此这篇关于c# autoresetevent和manualresetevent的实现示例的文章就介绍到这了,更多相关c# autoresetevent和manualresetevent内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com