繁体   English   中英

TaskCanceledException 与 ContinueWith

[英]TaskCanceledException with ContinueWith

我一直在试图弄清楚为什么我会因为一些最近开始出现异常的异步代码而收到 TaskCanceledException。 我已将我的问题简化为一个让我摸不着头脑的小代码片段:

static void Main(string[] args)
{
    RunTest();
}

private static void RunTest()
{
    Task.Delay(1000).ContinueWith(t => Console.WriteLine("{0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted).Wait();
}

据我所知,这应该只是暂停一秒钟然后关闭。 ContinueWith 不会被调用(这只适用于我的实际用例)。 但是,相反,我得到了一个 TaskCanceledException ,我不知道它来自哪里!

您使用了错误的任务继续选项:

请参阅以下链接: https : //msdn.microsoft.com/zh-cn/library/system.threading.tasks.taskcontinuationoptions%28v=vs.110%29.aspx

它说:指定仅在继续任务的前一个引发未处理的异常时才安排该继续任务。 此选项对于多任务继续无效。

我也收到了这个错误:

System.Threading.Tasks.TaskCanceledException:“任务已取消。”

代码块如下所示:

private void CallMediator<TRequest>(TRequest request) where TRequest : IRequest<Unit>
{
    _ = Task.Run(async () =>
    {
        var mediator = _serviceScopeFactory.CreateScope().ServiceProvider.GetService<IMediator>()!;
        await mediator.Send(request).ContinueWith(LogException, TaskContinuationOptions.OnlyOnFaulted);
    });
}

private void LogException(Task task)
{
    if (task.Exception != null)
    {
        _logger.LogError(task.Exception, "{ErrorMessage}", task.Exception.Message);
    }
}

阅读ContinueWith方法的文档,它有以下备注:

在当前任务完成之前,不会安排返回的任务执行。 如果不满足通过continuationOptions参数指定的延续条件,则延续任务将被取消而不是计划。

所以对我来说,它调用了第一个任务( mediator.Send(request) ),然后它继续执行任务ContinueWith(...) ,这是我await的任务。 但是,由于第一个任务没有发生异常,所以第二个任务被取消了。 因此,在等待第二个任务时,它抛出了一个TaskCanceledException

我所做的是将代码更改为:

private void CallMediator<TRequest>(TRequest request) where TRequest : IRequest<Unit>
{
    _ = Task.Run(async () =>
    {
        var mediator = _serviceScopeFactory.CreateScope().ServiceProvider.GetService<IMediator>()!;
        try
        {
            _ = await mediator.Send(request);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "{ErrorMessage}", ex.Message);
        }
    });
}

我没有使用.ContinueWith(...) ,而是将其替换为常规的 try-catch 块,以防我感兴趣的任务失败。 我认为这简化了代码并使其更具可读性。

在问题中,有这行代码:

Task.Delay(1000).ContinueWith(t => Console.WriteLine("{0}", t.Exception), TaskContinuationOptions.OnlyOnFaulted).Wait();

我会将其重写为:

try
{
    Task.Delay(1000).Wait();
}
catch (Exception ex)
{
    Console.WriteLine("{0}", ex);
}

就像上面的家伙说的那样,此调用只需要处于故障状态的前提任务,否则将抛出TaskCanceledException,对于这种具体情况,您可以泛化ContinueWith来处理所有状态:

await Task.Delay(1000).ContinueWith(
    task => 
        {
            /* take into account that Canceled-task throw on next row the TaskCancelledException */

            if (!task.IsFaulted) {
                return;
            }

            Console.WriteLine("{0}", task.Exception);
            // do smth like 'throw task.Exception.InnerException'
        });

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM