任务.WhenAny - 一个任务被取消

4
在以下代码中:
        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);
        }

有时我会在调用await task.ContinueWith时得到 A task was cancelled 的提示。我的目标是查看是否在100毫秒内完成任务 - 如果没有,我想处理一些日志记录(这个特定的任务存在资源泄漏问题,因此我正在尝试通过设置超时来解决它)。这是从这里提取的指导:Debugging Task.WhenAny and Push Notifications 为什么会发生这种情况,我该如何防止这个异常被抛出?
2个回答

6
这种情况发生在您的任务无法在规定时间内(100毫秒)完成,但稍后可以完成。您使用TaskContinuationOptions.OnlyOnFaulted运行您的继续任务,如果原始任务未出现故障,则此类任务将被取消。您需要await ContinueWith的结果,因此,如果您的任务没有出现故障,则会取消您的继续任务并出现异常。

一般来说,这种处理超时的方式并没有太多意义,因为即使超时已到达,您仍然要等待原始任务完成以记录异常。我认为您需要在继续任务之前删除await。然后,代码将在超时时继续运行,但如果任务稍后失败,它将被记录。

您的代码中还存在另一个问题-Task.WhenAny永远不会抛出异常。所以这个条件:

await Task.WhenAny(task, Task.Delay(100)) == task

仅仅因为task已经完成并不意味着成功,因为task可能存在故障。即使WhenAny表明您的任务已经完成,也始终要检查task.Statustask.Exception。顺便提一下,您在问题中链接的那个答案也提到了这一点。

更新:如果你不喜欢VS警告没有等待调用 - 你可以禁用它针对这一特定行,或者使用类似这样的扩展方法:

static class TaskExtensions
{
    public static void Forget(this Task task)
    {
        // do nothing
    }
}

然后:
task.ContinueWith(s => {
    Logger.Write(s.Exception);
}, TaskContinuationOptions.OnlyOnFaulted).Forget();

在这种特定情况下进行这样做没有任何问题,VS只是在每个可能的可等待调用上发出警告,但未被等待。

我不是完全清楚。鉴于我正在尝试处理一个泄漏的API,您会建议如何处理而不抛出任何异常(仅记录日志)? - SB2055
更新后的答案 - 只需在task.ContinueWith前删除await即可。 - Evk
然后VS抱怨我没有等待异步调用。 - SB2055
@SB2055 再次更新。 - Evk

0

如果任务未运行,则会取消任务继续。您的任务继续将仅在故障时运行。如果它没有故障,那么继续将被取消。

不涉及处理特定异常的两个选择是要么不等待任何内容(并且不知道任务是否已完成运行),要么等待原始任务。一旦等待完成,任务继续将在该点运行或被取消。

如果您可以处理取消,则只需根据需要在任务继续上捕获TaskCancelledException(或适当的AggregateException)并丢弃异常即可。

在我看来,对于这种特定情况(记录异常),最好的方法就是再次等待原始任务并处理TaskCancelledException,无需任务继续,完全符合异步/等待规范。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接