You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在值未维持延迟时长时终止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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 09:12:17