【问题标题】:IsCancellationRequested is always false if CancellationTokenSource with delay如果 CancellationTokenSource 有延迟,则 IsCancellationRequested 始终为 false
【发布时间】:2021-12-24 18:51:07
【问题描述】:

我尝试通过带有计时器的 CancellationToken 来停止进程。

但 IsCancellationRequested 始终为 false。

我尝试拨打cancellationToken.ThrowIfCancellationRequested(); 不起作用。

public async Task<IReadOnlyList<ISearchableDevice>> SearchAsync(CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        await Task.Delay(100, cancellationToken);
        cancellationToken.ThrowIfCancellationRequested(); // doesn't work
    }

    IReadOnlyList<ISearchableDevice> devices = new List<ISearchableDevice>();
    return devices;
}

private void OnStartSearchCommandExecuted(object? p)
{
    using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)))
    {
        try
        {  
             InProgress = true;
             DeviceSearcher.SearchAsync(cts.Token)
                 .ContinueWith(task =>
                 {
                  InProgress = false;

                 }, cts.Token);
         }                
         catch (Exception)
         {
                    // TODO: Add exception handling
         }
    }           
}

错在哪里?

【问题讨论】:

  • 尝试在SearchAsync 调用中使用await 而不是ContinueWith。现在你的cts 在超时之前就被处理掉了。
  • @Serg 我认为你是对的,但我应该使用 async void,因为我的命令不是异步的
  • @Serg 你是对的。谢谢你。你可以填写答案
  • “但我应该使用 async void,因为” - 结束该句子的正确方法非常少。几乎任何时候你输入“async void”,你都在制造一个问题。
  • @Joshua 没有任何改变

标签: c# cancellationtokensource


【解决方案1】:

问题是cts 在达到超时之前就被释放了。更准确地说,它是在SearchAsync 到达第一个await 之后立即处理的。 你至少有两种方法可以解决这个问题

  1. 使用await DeviceSearcher.SearchAsync 而不是DeviceSearcher.SearchAsync().ContinueWith()。这是最好的方法,但它会迫使您使 OnStartSearchCommandExecuted 异步。

  2. 删除using块并在.ContinueWith中手动调用Dispose(并在ContinueWith中移动其他后搜索逻辑,也不要忘记在异常情况下调用Dispose(同步时是否会发生异常) SearchAsync 的一部分(在第一个 await 之前)或异步部分。))。

【讨论】:

  • 一般来说Task.ContinueWith() 通常是个坏主意。
  • @Aron 为什么这是个坏主意?
  • 我想,我可以使用事件。当搜索任务取消时,我调用事件。在事件处理方法中进行下一项工作(如果需要,使用调度程序)
  • 继续使用块、try/catch/finally 块、堆栈跟踪、SynchronizationContext(以及 WPF/WinForms/Blazor)进行中断。这些只是一些事情只会出错的例子,而不是落入成功的深渊......
  • async/await 做了很多有助于落入成功深渊的事情Task.ContinueWith 没有做。例如,您的 try/catch 在您的示例中完全没有做任何事情。此外,假设 WPF,ContinueWith 不会将回调编组回 Dispatcher,这可能会导致问题...
猜你喜欢
  • 1970-01-01
  • 2022-01-02
  • 2021-05-07
  • 1970-01-01
  • 2012-03-29
  • 2015-03-08
  • 1970-01-01
  • 1970-01-01
  • 2018-12-04
相关资源
最近更新 更多