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

如何正确实现CancellationTokenSource的取消与释放以避免ObjectDisposedException

问题根因

你遇到的ObjectDisposedException由以下几个逻辑缺陷共同导致:

  • CancellationTokenSource(以下简称cts)释放时机过早:你在LoopAndCheckPingAsync的ContinueWith回调中直接释放cts,但此时Ping类的底层异步回调仍持有cts关联的等待句柄引用,后续回调执行时访问已释放的安全句柄就触发了异常,和你贴出的堆栈信息完全吻合。
  • 静态cts字段存在线程安全隐患:如果多次调用批量Ping的启动逻辑,会出现cts实例被覆盖、新旧实例混用的问题,进一步放大了资源释放的时序问题。
  • 释放逻辑覆盖不全:仅在任务正常完成时释放cts,故障、取消场景下的cts资源没有被正确释放,既存在资源泄漏风险,也可能触发其他时序问题。
  • ContinueWith本身的执行逻辑缺陷:没有指定任务调度器、也没有等待底层Ping资源完全清理就触发释放逻辑,进一步提前了cts的释放时机。

修复方案

1. 核心调整原则

  • 优先用async/await代替ContinueWith,简化异步流程控制,确保所有异步操作(包括底层非托管资源清理)完全完成后再执行资源释放。
  • 不要将cts设为静态字段,每次执行批量Ping任务时创建独立的cts实例,避免多轮任务的资源互相干扰。
  • 取消操作先调用cts.Cancel(),等待所有异步操作响应取消完成后再执行释放,给底层资源清理留足时间。
  • 用using语句包裹cts实例,无论任务正常完成、故障、取消,都会自动触发释放,无需手动处理释放逻辑。

2. 修复后参考代码

/// 批量Ping入口方法
private async Task RunBatchPingAsync(List<string> ipList)
{
    IsRun = true;
    // 每次任务创建独立cts,using会在作用域结束后自动释放
    using var cts = new CancellationTokenSource();
    var cancellationToken = cts.Token;
    
    // 如需支持外部主动取消任务,可将cts实例暴露给外部调用方,外部调用cts.Cancel()即可触发取消
    try
    {
        await LoopAndCheckPingAsync(ipList, cancellationToken);
    }
    catch (OperationCanceledException)
    {
        Global.LOG.Log("Sonar.Start() - 批量Ping任务已取消");
    }
    catch (Exception ex)
    {
        // 解包聚合异常获取真实错误信息
        while (ex is AggregateException aggEx && aggEx.InnerException != null)
            ex = ex.InnerException;
        Global.LOG.Log($"Sonar.Start() - 执行异常:{ex.Message}");
    }
    finally
    {
        IsRun = false;
    }
}

/// 批量Ping执行逻辑,需传入CancellationToken
private async Task LoopAndCheckPingAsync(List<string> ipList, CancellationToken ct)
{
    // 示例:每个Ping操作绑定取消令牌,且用using包裹及时释放Ping自身资源
    var pingTasks = ipList.Select(ip =>
    {
        using var ping = new Ping();
        // 取消令牌传入Ping异步方法
        return ping.SendPingAsync(ip, 1000, ct);
    });
    await Task.WhenAll(pingTasks);
    
    // 保留你原有业务逻辑即可
}

3. 特殊场景兼容

如果确实需要保留静态cts实现全局统一取消,需要加锁保护cts的创建、取消、释放流程,避免多线程竞争:

private static readonly object _ctsLock = new object();
private static CancellationTokenSource _cts;

// 全局取消方法
public static void StopBatchPing()
{
    lock (_ctsLock)
    {
        _cts?.Cancel();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 09:45:02