在键盘事件中使用CancellationToken调用Task.Delay时的TaskCanceledException

And*_*rts 16 c# asynchronous cancellationtokensource windows-runtime cancellation-token

我试图延迟在WinRT中从键盘事件调用的方法(在示例中为SubmitQuery())的处理,直到一段时间内没有其他事件(在这种情况下为500毫秒).

我只想在我认为用户输入完成后运行SubmitQuery().

使用下面的代码,当Task.Delay(500,cancellationToken.Token)时,我不断收到System.Threading.Tasks.TaskCanceledException; 叫做.我在这做错了什么?

CancellationTokenSource cancellationToken = new CancellationTokenSource();

private async void SearchBox_QueryChanged(SearchBox sender, SearchBoxQueryChangedEventArgs args)
{

        cancellationToken.Cancel();
        cancellationToken = new CancellationTokenSource();

    await Task.Delay(500, cancellationToken.Token);

    if (!cancellationToken.IsCancellationRequested)
    {
        await ViewModel.SubmitQuery();
    }
}
Run Code Online (Sandbox Code Playgroud)

Jer*_*gen 44

如果ContinueWith()使用空操作添加,则不会抛出异常.异常被捕获并传递给该task.Exception属性ContinueWith().但它可以帮助您避免编写试图/捕获代码的问题.

await Task.Delay(500, cancellationToken.Token).ContinueWith(tsk => { });
Run Code Online (Sandbox Code Playgroud)

  • 那真棒,谢谢.保存一些非常难看的try/catch代码:) (6认同)
  • 谢谢你!在调试器中非常烦人,即使它被捕获了.我认为在这些`.Delay`情况下我们**期望**令牌被取消所以异常甚至不应该被提升/捕获,这完全解决了这个问题. (6认同)
  • @Alexsandro那是因为它被捕获并且异常在 `Task<TResult> tsk` 参数中传递。`.ContinueWith(tsk => tsk.Exception);` 这样继续任务就可以处理异常。在这种情况下我们不.... (2认同)
  • 我认为应该注意的是,ContinueWith 方法确实捕获并吸收了异常,但是 task.delay 之后的任何代码仍然会执行。因此,如果您在异步任务方法中调用它,这可能不是一个好主意,因为取消令牌的目的是取消当前任务。在这种情况下,您必须手动检查 IsCancellationRequested 的取消令牌,或者只是将 try/catch 留在那里。 (2认同)

Ste*_*ary 16

这是可以预料的.取消旧版时Delay,会引发异常; 这就是取消的方式.你可以放一个简单的try/ catch围绕它Delay来捕获预期的异常.

请注意,如果你想做这样的基于时间的逻辑,Rx比自然更适合async.

  • @Anupam:这是我自己的经历. (2认同)

The*_*ias 6

抑制等待任务异常的另一种简单方法是将任务作为单个参数传递给Task.WhenAny

创建一个任务,该任务将在任何提供的任务完成后完成。

await Task.WhenAny(Task.Delay(500, token)); // Ignores the exception
Run Code Online (Sandbox Code Playgroud)

这种方法的一个问题是它没有清楚地传达其意图,因此建议添加注释。另一个是它会导致分配(因为 的params签名Task.WhenAny)。


注意:当 aTask发生故障并且Exception未直接或间接观察到它时,TaskScheduler.UnobservedTaskException会在未来某个时刻(非确定性)触发此任务的事件。上面显示的技巧,使用该Task.WhenAny方法,不观察任务Exception,因此事件被触发。在 的特定情况下Task.Delay(500, token),任务以取消状态完成,而不是以故障状态完成,因此无论如何它都不会触发事件。


lil*_*lo0 6

我认为值得添加一个评论来说明为什么它会这样工作。

该文档实际上是错误的,或者对于方法的 TaskCancelledException 写得不清楚Task.Delay。该Delay方法本身永远不会抛出该异常。它将任务转移到取消状态,而到底引发异常的是awaitTask.Delay这里使用什么方法并不重要。它与任何其他已取消的任务的工作方式相同,这就是取消的预期工作方式。这实际上解释了为什么添加延续会神秘地隐藏异常。因为它是由 引起的await