在以下代码中:
if (await Task.WhenAny(task, Task.Delay(100)) == task) {
success = true;
}
else {
details += "Timed out on SendGrid.";
await task.ContinueWith(s =>
{
LogError(s.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
}
我偶尔会A task was cancelled
致电await task.ContinueWith
。我的目标是查看task
是否在100毫秒内完成 - 如果没有,我想处理一些日志记录(这个特定任务有资源泄漏,所以我试图通过将其包装起来解决它超时)。这是从这里的指导中提取的:Debugging Task.WhenAny and Push Notifications
为什么会发生这种情况,我该怎么做才能防止抛出此异常?
答案 0 :(得分:6)
当你的task
无法及时完成(100毫秒)时会发生这种情况,但以后能够完成。你使用TaskContinuationOptions.OnlyOnFaulted
继续运行,如果原始任务没有出现故障,这些任务就会被取消。 await
ContinueWith
await
的结果,如果您的任务没有出现问题,您的续例将被取消,并且您有例外情况。
通常这种处理超时的方法没有多大意义,因为即使在达到超时之后,您仍然要等到原始任务完成,才能记录异常。我认为你必须在继续之前删除Task.WhenAny
。然后代码将在超时时继续,但如果任务稍后失败 - 它将被记录。
您的代码中还有另一个问题 - await Task.WhenAny(task, Task.Delay(100)) == task
永远不会抛出。所以这个条件:
task
并不意味着成功,因为task.Status
可能会出现问题。即使task.Exception
表示您的任务已完成,也请务必检查WhenAny
和static class TaskExtensions
{
public static void Forget(this Task task)
{
// do nothing
}
}
。顺便说一句,您在问题中链接的答案提到了这一点。
更新:如果您不喜欢关于未等待电话的VS警告 - 您可以为此特定行禁用它,或使用这样的扩展方法:
task.ContinueWith(s => {
Logger.Write(s.Exception);
}, TaskContinuationOptions.OnlyOnFaulted).Forget();
然后:
node_modules/.bin/karma
这样做没有任何害处(当然在这种特殊情况下),VS只是在每个可能等待的等待的电话上发出此警告。
答案 1 :(得分:0)
如果未运行任务延续,则会取消它们。您的任务继续将运行OnlyOnFaulted。如果没有错,则继续将被取消。
您的两个不涉及处理该特定异常的选择是要么不等待任何事情(并且不知道Task是否已经完成运行),要么等待原始任务。等待完成后,任务继续将在该点运行或取消。
如果你处理取消很好,那么只需根据需要捕获任务继续的TaskCancelledException(或者适当的AggregateException)并丢弃异常。
在我看来,对于这种特定情况(记录异常),最好的方法是再次等待原始任务并处理TaskCancelledException,不需要任务继续并且完全异步/等待兼容。
答案 2 :(得分:0)
正如@Evk已经提到的那样,你的Continuation
被取消了。但我想补充一点,延续可以被视为配置的一部分,因此在Task
生成时设置。请考虑以下事项:
var task = Task.Delay(500).ContinueWith(s =>
{
LogError(s.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
if (await Task.WhenAny(task, Task.Delay(100)) == task) {
success = true;
} else {
details += "Timed out on SendGrid.";
}
在这种方法中,您的异常仍然会被记录,并且您的逻辑将继续只知道Task
超时。如果除了超时之外,其他逻辑确实需要了解Exception
,那么您需要再次await
Task
进行讨论。
为清晰起见而更新
原始代码(1)在此阶段,我们无法看到task
的定义位置。
//task is undeclared in this snippet
if (await Task.WhenAny(task, Task.Delay(100)) == task) {
success = true;
}
else {
details += "Timed out on SendGrid.";
await task.ContinueWith(s =>
{
LogError(s.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
}
(2)让我们添加一个模拟任务,例如
var task = Task.Delay(500); //defines task as a Task that will complete in 500ms
if (await Task.WhenAny(task, Task.Delay(100)) == task) {
success = true;
}
else {
details += "Timed out on SendGrid.";
await task.ContinueWith(s =>
{
LogError(s.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
}
(3)接下来,当我们await task.ContinueWith
时,我们允许抛出TaskCancelledException
。如果删除await
,则可以忽略Exception
,但我们会收到一条未等待任务的警告。我们可以忽略该警告,或者我们可以认识到continuation
可以被视为特定Task
配置的一部分。有了这个,我们可以使用适当的选项配置我们创建任务的Continuation
,TaskContinuationOptions.OnlyOnFaulted
:
//Add continuation configuration where task is created
var task = Task.Delay(500).ContinueWith(s =>
{
LogError(s.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
if (await Task.WhenAny(task, Task.Delay(100)) == task) {
success = true;
} else {
details += "Timed out on SendGrid.";
//removed continuation from here.
}