You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现AutoResetEvent等待并支持调用取消后终止等待?

实现可取消的事件等待:方案分析与优化建议

你的核心需求是在等待外部事件触发的过程中,能够通过CancellationToken主动终止等待,下面先分析你给出的两个方案的问题,再提供更简洁优雅的实现方式。

方案1分析

基于AutoResetEvent+CancellationToken.Register的方案存在三个明显问题:

  • 内存泄漏风险:通过cancellationToken.Register注册的回调未被取消注册,若CancellationToken生命周期长于MyClass,会导致_dataArrivedSignal被回调持有无法被GC回收;
  • 取消响应滞后:仅在WaitOne返回后才检查取消信号,若取消在DoSomeWork执行期间触发,无法立即终止循环;
  • 同步与异步模型混用:用Task.Factory.StartNew启动同步阻塞的循环,未发挥异步编程的资源利用优势。

方案2分析

基于TaskCompletionSource的方案同样存在缺陷:

  • 线程安全隐患:_waiter字段与_awaiters队列的同步逻辑不严谨,多线程场景下可能出现等待的TaskCompletionSource被重复设置或遗漏的情况;
  • 阻塞调用浪费资源:使用waiter.Task.Wait(cancellationToken)属于同步阻塞,未利用异步模型的非阻塞特性;
  • 冗余设计:_awaiters队列与_waiter字段职责重叠,逻辑可进一步简化。

更优雅的实现方式

推荐使用**异步等待+TaskCompletionSource结合CancellationToken**的方式,既避免线程阻塞,又能高效响应取消信号,同时解决资源泄漏问题。

实现思路

  1. 将事件触发转换为可等待的Task,用TaskCompletionSource作为事件与异步代码的桥梁;
  2. 利用CancellationToken.Register在取消时完成TaskCompletionSource,同时通过using管理回调注册,确保资源释放;
  3. 使用await替代同步阻塞,充分利用线程池资源;
  4. 复用TaskCompletionSource(或在每次等待前重置),避免频繁创建对象。

代码示例(通用.NET版本)

class MyClass
{
    private TaskCompletionSource<bool> _tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
    private readonly object _lockObj = new object();

    public async Task RunAsync(CancellationToken cancellationToken)
    {
        while (/* 你的循环条件 */ true)
        {
            DoSomeWork();

            // 注册取消回调,并用using确保回调被释放
            using var registration = cancellationToken.Register(() =>
            {
                lock (_lockObj)
                {
                    _tcs.TrySetCanceled();
                }
            });

            try
            {
                await _tcs.Task;
            }
            finally
            {
                // 重置TCS用于下一次等待
                lock (_lockObj)
                {
                    _tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
                }
            }
        }
    }

    private void OnDataArrived(EventArgs args)
    {
        lock (_lockObj)
        {
            _tcs.TrySetResult(true);
        }
    }

    private void DoSomeWork()
    {
        // 你的业务逻辑实现
    }
}

.NET 5+简化版(利用官方异步同步原语)

如果使用.NET 5及以上版本,可直接使用AsyncManualResetEvent(需引用System.Threading.Tasks.Dataflow包),代码更简洁且线程安全:

class MyClass
{
    private readonly AsyncManualResetEvent _asyncResetEvent = new AsyncManualResetEvent(false);

    public async Task RunAsync(CancellationToken cancellationToken)
    {
        while (/* 你的循环条件 */ true)
        {
            DoSomeWork();

            // 等待事件触发或取消信号
            await _asyncResetEvent.WaitAsync(cancellationToken);
            
            // 重置信号,准备下一次等待
            _asyncResetEvent.Reset();
        }
    }

    private void OnDataArrived(EventArgs args)
    {
        _asyncResetEvent.Set();
    }

    private void DoSomeWork()
    {
        // 你的业务逻辑实现
    }
}

关键优化点

  • 异步非阻塞:用await替代同步阻塞,避免占用线程池线程;
  • 资源安全:通过using管理CancellationTokenRegistration,避免内存泄漏;
  • 线程安全:用锁或官方原语保护同步操作,避免多线程冲突;
  • 即时响应取消:取消信号触发时立即终止等待,无需等待阻塞操作完成。

内容的提问来源于stack exchange,提问作者theateist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 23:50:25