C#类Dispose方法如何延迟执行,等待后台任务完成后再清理?
C# Dispose方法内置超时等待后台任务的优雅实现
实现思路
核心遵循Dispose方法的设计约定:不对外抛出异常、内部逻辑对调用方透明,同时支持最多5秒的后台任务等待窗口期,超时后强制进入清理流程。
方案1:同步Dispose实现(兼容所有.NET版本)
适合没有异步调用要求、Dispose调用线程允许短暂阻塞的场景:
public class A : IDisposable { // 内部持有后台任务引用 private Task _backgroundTask; // 可选:用于通知后台任务提前终止的令牌源 private readonly CancellationTokenSource _workCts = new CancellationTokenSource(); private bool _disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { // 先给后台任务发终止信号(支持优雅停止的场景用) _workCts.Cancel(); try { // 等待最多5秒,不管任务状态如何超时就继续 _backgroundTask?.Wait(TimeSpan.FromSeconds(5)); } catch (AggregateException) { // 吞掉后台任务的所有异常,Dispose不允许对外抛异常 // 内部可在此处添加日志逻辑记录故障 } // 此处放置原有资源清理逻辑 _workCts.Dispose(); } _disposed = true; } // 示例:内部启动后台任务的方法 public void StartBackgroundWork() { _backgroundTask = Task.Run(async () => { while (!_workCts.Token.IsCancellationRequested) { // 你的业务后台逻辑 await Task.Delay(1000, _workCts.Token); } }, _workCts.Token); } }
方案2:异步Dispose实现(.NET Core 3.0+ / .NET 5+ 推荐)
适合UI、Web服务等不能阻塞调用线程的场景,同时兼容同步调用兜底:
public class A : IDisposable, IAsyncDisposable { private Task _backgroundTask; private readonly CancellationTokenSource _workCts = new CancellationTokenSource(); private bool _disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public async ValueTask DisposeAsync() { await DisposeAsyncCore(); Dispose(false); GC.SuppressFinalize(this); } protected virtual async ValueTask DisposeAsyncCore() { if (_disposed) return; _workCts.Cancel(); try { if (_backgroundTask != null) { // 异步等待最多5秒,不会阻塞调用线程 using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var completedTask = await Task.WhenAny(_backgroundTask, Task.Delay(Timeout.Infinite, timeoutCts.Token)); // 任务提前完成的话可以观察异常,超时则直接走清理 if (completedTask == _backgroundTask) await _backgroundTask; } } catch (OperationCanceledException) { } catch (AggregateException) { } // 此处放置异步资源清理逻辑 _workCts.Dispose(); _disposed = true; } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { // 同步调用的兜底逻辑 _workCts.Cancel(); try { _backgroundTask?.Wait(5000); } catch { } _workCts.Dispose(); } _disposed = true; } }
注意要点
- 超时时间选择:5秒属于较长的Dispose等待时长,业务允许的话建议尽量缩短超时阈值,避免影响调用方的执行效率
- 资源访问安全:如果后台任务会访问类内的托管资源,必须等待超时或任务结束后再释放对应资源,避免出现
ObjectDisposedException - 异常处理:所有Dispose内部捕获的异常建议添加内部埋点日志,避免后台故障完全无感知
内容的提问来源于stack exchange,提问作者sOltan
相关产品推荐
相关产品推荐

