如何不使用类全局CancellationTokenSource实现带超时的单事件等待?
问题描述
我当前的实现依赖类全局的CancellationTokenSource来实现带超时的单事件等待功能,请问能不能不借助全局的CancellationTokenSource达成同样的效果?
现有代码如下:
CancellationTokenSource _waitCTS = null; private async Task WaitWithTimeout(string param) { _waitCTS?.Dispose(); _waitCTS = new CancellationTokenSource(); XClass x = new XClass(param); x.OnReceived += X_OnReceived; try { await Task.Delay(5000, _waitCTS.Token).ConfigureAwait(true); } catch (TaskCanceledException) { Debug.WriteLine("Timed out"); } x.OnReceived -= X_OnReceived; x.Cleanup(); } private void X_OnReceived(object sender, SelectedItemEventArgs e) { Debug.WriteLine(e.ReceivedItem); if (_waitCTS != null) _waitCTS.Cancel(); }
解决方案
当然可以,用TaskCompletionSource<T>就能替代全局CancellationTokenSource,把等待逻辑和事件回调封装在方法内部,彻底摆脱全局状态的依赖。
改造后的代码示例:
private async Task WaitWithTimeout(string param) { using var tcs = new TaskCompletionSource<bool>(); XClass x = new XClass(param); // 定义局部事件回调,直接访问当前方法内的tcs void OnReceivedHandler(object sender, SelectedItemEventArgs e) { Debug.WriteLine(e.ReceivedItem); tcs.TrySetResult(true); } x.OnReceived += OnReceivedHandler; try { // 同时等待事件触发和超时,哪个先完成就执行对应逻辑 var completedTask = await Task.WhenAny(tcs.Task, Task.Delay(5000)).ConfigureAwait(true); if (completedTask != tcs.Task) { Debug.WriteLine("Timed out"); } } finally { // 无论结果如何,都要解绑事件并清理资源 x.OnReceived -= OnReceivedHandler; x.Cleanup(); } }
核心改动说明
- 去掉全局变量:用局部的
TaskCompletionSource<bool>替代全局CancellationTokenSource,事件触发时通过TrySetResult标记任务完成,不需要取消操作。 - 局部回调绑定:把事件回调定义为方法内部的局部委托,直接访问当前方法的
tcs,避免依赖外部状态。 - 更简洁的等待逻辑:用
Task.WhenAny同时监听事件完成和超时任务,无需捕获异常来判断超时,代码更直观。 - 可靠的资源清理:用
finally块确保事件解绑和资源清理,不管是事件触发还是超时,都会执行这部分逻辑,比原写法更稳妥。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

