简体   繁体   English

从Task.ContinueWith抛出异常到主线程

[英]Throwing exception from Task.ContinueWith to the main thread

In an ASP.NET Core Controller -> I'm trying to rethrow an exception from a Task (in order to log the exception), but I don't understand why it's not caught by the global controller exception filter. 在ASP.NET Core Controller中->我试图从Task中抛出一个异常(以便记录该异常),但是我不明白为什么全局控制器异常过滤器无法捕获该异常。

public IActionResult MyAction()
{
    Task task = GetMyTask();

    SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());

    task.ContinueWith(t =>
        {
            if (t.IsFaulted && t.Exception != null && t.Exception.InnerException != null)
            {
                throw t.Exception.InnerException; // Code reached, but this exception is NOT handled by the controller global error handler
            }
        }, cancellationToken: CancellationToken.None, continuationOptions: TaskContinuationOptions.OnlyOnFaulted, scheduler: TaskScheduler.FromCurrentSynchronizationContext() );

    throw new Exception("This exception is handled by the controller global error handler");
    return Ok();
}

I don't want to use await , since this task is run-and-forget. 我不想使用await ,因为此任务是一劳永逸的。

The await keyword has pretty much one job: it holds the processing of the code until the task returns. 关键字await几乎完成了一项工作:它保留对代码的处理,直到任务返回为止。 It also unwraps the task, but just as a convenience. 它还可以解包任务,但只是为了方便。

Since you're not using await here, that means the calling code moves on and your response it returned while the task is still completing. 由于您此处未使用await ,这意味着调用代码将继续运行,并且在任务仍完成时返回响应。 You're essentially taking it out of the request pipeline context, which then means you cannot catch any exceptions it throws - it's fire and forget, emphasis on the forget . 您实际上是将其从请求管道上下文中删除,这意味着您无法捕获它引发的任何异常-着火了,忘了,重点是忘了

This is not the same thing as doing something "in the background". 这与在“后台”执行操作不同。 If you're trying to handle some long-running process outside of the request, the solution to that is to schedule it and have processed by something like an IHostedService or a library like Hangfire. 如果您尝试处理请求之外的某些长时间运行的流程,则解决方案是安排该请求并由IHostedService类的IHostedService或像Hangfire这样的库进行处理。 That way your request can go ahead an return, but you're now doing the work in a monitored context, where you can do things like throw exceptions. 这样,您的请求可以继续返回,但是现在您正在受监视的上下文中进行工作,在这里您可以执行诸如引发异常之类的事情。 However, you need to be more careful at that point, because the exception still won't bubble up to your global exception handler, because there's no request in play. 但是,此时您需要格外小心,因为异常仍然不会出现在全局异常处理程序中,因为没有任何请求在进行中。 Instead, you'd need to catch the exception(s) and then notify the client via something like SignalR (websockets), log it, etc. This will also allow you to then potentially monitor the status of the process, and report back to the user (again via SignalR) both progress and completion. 相反,您需要捕获异常,然后通过诸如SignalR(websockets)之类的方式通知客户端,将其记录下来。这也将使您可以潜在地监视流程的状态,并向其报告用户(再次通过SignalR)进行进度和完成进度。 None of that is possible by just having a task run without awaiting it. 仅运行任务而不等待它,这是不可能的。

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

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