如何在值未维持延迟时长时终止Task.Delay()?
问题分析与修复
你的核心问题在于每次调用DataWasReceivedAsync时都会创建新的CancellationTokenSource实例,这导致:
- 当触发1分钟延迟后,后续修改值再次调用方法时,新的
tokenSource.Cancel()只能取消当前方法内的token,完全影响不到之前已经开始执行的延迟任务。 - 之前的延迟任务不受任何控制,时间到了就会执行
ActivateGoldArrow(),和当前的数值状态无关。
修复步骤
1. 将CancellationTokenSource改为类成员变量
把tokenSource从方法内部移到类的级别,确保所有调用共享同一个取消令牌源,这样后续的取消操作能终止之前的延迟任务。
2. 处理取消异常与资源释放
异步任务被取消时会抛出OperationCanceledException,需要捕获避免程序崩溃;同时在取消后要释放旧的tokenSource资源,再创建新的实例供后续使用。
3. 线程安全控制
因为DataWasReceivedAsync可能被多线程调用,需要用lock确保tokenSource的操作是线程安全的。
修复后的代码示例
private CancellationTokenSource _tokenSource; private readonly object _lockObj = new object(); public async void DataWasReceivedAsync() { lock (_lockObj) { // 先取消之前的任务(如果有的话) _tokenSource?.Cancel(); _tokenSource?.Dispose(); _tokenSource = new CancellationTokenSource(); } if (windSpeed < 3) { HideData(); return; } if (tas > 40 && (attitudeRoll > 3 || attitudeRoll < -3)) { try { await Task.Delay(TimeSpan.FromMinutes(1), _tokenSource.Token); // 检查是否被取消,没取消才执行后续操作 _tokenSource.Token.ThrowIfCancellationRequested(); ActivateGoldArrow(); } catch (OperationCanceledException) { // 任务被取消,无需处理 } } else if (tas < 40) { ActivateGoldArrow(); } else { ActivateGreenArrow(); } }
额外说明
- 调整了条件判断的括号:原代码
tas > 40 && attitudeRoll > 3 || tas > 40 && attitudeRoll < -3简化为tas > 40 && (attitudeRoll > 3 || attitudeRoll < -3),逻辑更清晰且避免歧义。 - 每次进入方法都先取消并释放旧的
tokenSource,确保之前的延迟任务被终止,不会再执行后续的ActivateGoldArrow()。 - 使用
lock保证多线程环境下_tokenSource的操作不会出现竞态条件。
内容的提问来源于stack exchange,提问作者simone paolucci
相关产品推荐
相关产品推荐

