如何消除TaskCanceledException,实现事件触发后延迟执行并重启延迟
实现事件延迟触发并重置延迟的解决方案
需求:当事件触发时,延迟一段时间后执行目标方法,且后续触发的事件需要重置这个延迟。例如滚动条位置变化时,需在用户停止滚动1秒后更新视图。
尝试用Task.Delay+ContinueWith+取消令牌实现第一种方案时,后续调用函数会抛出TaskCanceledException异常,以下是修复后的可行版本,同时附上已验证可用的替代方案。
原第一种方案代码
private CancellationTokenSource? _cts; private async void Update() { _cts?.Cancel(); _cts = new(); await Task.Delay(TimeSpan.FromSeconds(1), _cts.Token) .ContinueWith(o => Debug.WriteLine("Update now!"), TaskContinuationOptions.OnlyOnRanToCompletion); }
修复后的第一种方案代码
private CancellationTokenSource? _cts; private async void Update() { _cts?.Cancel(); using var newCts = new CancellationTokenSource(); _cts = newCts; try { await Task.Delay(TimeSpan.FromSeconds(1), newCts.Token); if (!newCts.Token.IsCancellationRequested) { Debug.WriteLine("Update now!"); } } catch (TaskCanceledException) { // 捕获预期的取消异常,无需额外处理 } finally { if (_cts == newCts) { _cts = null; } } }
修复要点
- 用
using声明自动管理CancellationTokenSource的生命周期,避免资源泄漏 - 捕获
TaskCanceledException,阻止异常扩散到全局 - 执行目标方法前检查令牌状态,确保只有未被取消的延迟任务才会触发逻辑
- 在finally块中清理
_cts引用,避免后续操作访问已释放的对象
可行的替代方案代码
private CancellationTokenSource? _cts; private CancellationTokenRegistration? _cancellationTokenRegistration; private void Update() { _cancellationTokenRegistration?.Unregister(); _cts = new(); _cancellationTokenRegistration = _cts.Token.Register(() => Debug.WriteLine("Update now!")); _cts.CancelAfter(1000); }
替代方案逻辑
每次触发事件时,先取消之前的回调注册,再创建新的CancellationTokenSource,注册目标方法后设置1秒延迟取消。当延迟结束令牌被取消时,自动执行注册的回调方法,全程不会抛出取消异常,实现逻辑更简洁。
内容的提问来源于stack exchange,提问作者radoslawik
相关产品推荐
相关产品推荐

