在C#异步编程中,CancellationTokenSource用于创建取消信号。当我们在代码中调用了Dispose方法释放资源之后,如果再次访问Token属性或者调用Cancel方法,运行时就会抛出ObjectDisposedException。这个问题在任务尚未结束就提前释放对象、或在多个线程间共享实例时尤为常见。

为什么会出现ObjectDisposedException
CancellationTokenSource实现了IDisposable接口,内部持有着等待句柄等系统资源。一旦调用Dispose,对象就进入已释放状态。此后任何试图触发取消或读取令牌的操作都会被认为非法。下面是一段容易出错的示例:
using System;
using System.Threading;
class Program
{
static void Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.Dispose();
// 下面这行会抛出 ObjectDisposedException
cts.Cancel();
}
}
避免异常的几种做法
1. 控制生命周期,不在释放后使用
最直观的方式是保证CancellationTokenSource的作用域覆盖所有使用它的代码。尽量不要提前调用Dispose,或者把Dispose放在所有相关任务完成之后。
2. 使用 IsCancellationRequested 与状态判断
如果必须复用对象,可以在调用前先判断状态,但注意已释放对象访问任何成员都会异常,因此更好的方式是自己维护一个是否已释放的标记。
using System;
using System.Threading;
class SafeCts
{
private CancellationTokenSource _cts = new CancellationTokenSource();
private bool _disposed = false;
public CancellationToken Token
{
get
{
if (_disposed)
throw new ObjectDisposedException(nameof(SafeCts));
return _cts.Token;
}
}
public void CancelSafe()
{
if (_disposed)
return;
_cts.Cancel();
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
_cts.Dispose();
}
}
}
3. 捕获异常作为兜底
在无法完全掌控调用时机的场景下,可以用try-catch包裹可能出错的调用,避免程序崩溃。
using System;
using System.Threading;
try
{
cts.Cancel();
}
catch (ObjectDisposedException)
{
// 对象已释放,忽略或记录日志
}
多线程场景的注意事项
当CancellationTokenSource被多个线程共享时,一个线程调用Dispose而另一个线程调用Cancel就会引发竞争。建议使用锁或者上面封装的SafeCts类来保证线程安全。同时在using块中要注意:using结束后对象即被释放,不要将它的Token传递给后台还在运行的任务。
总结
避免CancellationTokenSource的ObjectDisposedException,核心在于理清对象的生命周期,确保取消操作发生在释放之前。通过封装、状态判断和异常捕获,可以显著降低出错概率,让异步取消逻辑更加健壮。
CancellationTokenSourceObjectDisposedException异步取消修改时间:2026-07-28 17:54:31