在 ASP.NET 应用程序中,我有一个操作,当点击时,以下列方式启动一个新的后台任务:
Task.Factory.StartNew(async () => await somethingWithCpuAndIo(input), CancellationToken.None, TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning, TaskScheduler.FromCurrentSynchronizationContext());
我不是在等待它,我只是想让它开始,并在后台继续工作。在那之后,我立即向客户端返回响应。
但是出于某种原因,在执行后台工作的初始线程遇到等待方法完成的等待之后,在调试时,我成功地解析了该方法,但是在返回时,执行只是停止并且不会在该点以下继续。
有趣的是,如果我完全等待这个任务(使用 double await
),一切都会按预期进行。
这是由于 SynchronizationContext 吗?在我返回响应的那一刻,同步上下文是否被处理/删除?(方法内部正在使用 SynchronizationContext)
如果是因为这个,问题究竟发生在哪里?
A) 当调度器试图在给定的同步上下文上分配工作时,它已经被释放了,所以什么都不会提供
B)在方法执行的某个地方,当我向客户端返回响应时,同步上下文丢失,不管其他任何事情。
C) 完全是别的东西?
如果是 A),我应该能够通过Thread.Sleep()
在安排工作和返回响应之间简单地解决这个问题。(试过了,没用。)
如果是 B) 我不知道如何解决这个问题。帮助将不胜感激。
你的评论让你的意图更清晰一些。我认为你想做的是:
somethingWithCpuAndIo
方法可以访问请求上下文。但,
await
命中第一个,Task
就会返回 a,这也意味着Task.Factory.StartNew
返回并继续执行调用方法。这意味着响应返回给客户端。当请求完成时,上下文被释放。所以你不能真正做你想做的两件事。有几种方法可以解决这个问题:
首先,您可能根本无法在不同的线程上启动它。这取决于何时somethingWithCpuAndIo
需要访问上下文。如果它只需要first 之前await
的上下文,那么您可以执行以下操作:
public IActionResult MyAction(input) {
somethingWithCpuAndIo(input); //no await
}
private async Task somethingWithCpuAndIo(SomeThing input) {
// You can read from the request context here
await SomeIoRequest().ConfigureAwait(false);
// Everything after here will run on a ThreadPool thread with no access
// to the request context.
}
每个异步方法开始同步运行。当await
给出一个不完整的Task
. 所以在这个例子中,somethingWithCpuAndIo
将在请求上下文中的同一个线程上开始执行。当它击中 时await
, aTask
被返回到MyAction
,但它不会被等待,因此MyAction
完成执行并且在 SomeIoRequest()
完成之前将响应发送到客户端。但是ConfigureAwait(false)
告诉它我们不需要在相同的上下文中somethingWithCpuAndIo
恢复执行,所以在 ThreadPool 线程上恢复执行。
但是,如果您在第一个await
in之后不需要上下文,那只会对您有所帮助somethingWithCpuAndIo
。
您最好的选择是仍然在不同的线程上执行,但将您需要的值从上下文传递到somethingWithCpuAndIo
.
而且,出于此处详细描述的原因,请使用Task.Run
代替。Task.Factory.StartNew
更新:这很可能会导致不可预测的结果,但您也可以尝试将引用传递HttpContext.Current
给线程并HttpContext.Current
在新线程中进行设置,如下所示:
var ctx = HttpContext.Current;
Task.Run(async () => {
HttpContext.Current = ctx;
await SomeIoRequest();
});
但是,这完全取决于您如何使用上下文。HttpContext
本身没有实现IDiposable
,所以它本身不能被处理。只要您持有对它的引用,垃圾收集器就不会删除它。但是上下文的设计寿命并不比请求长。因此,在将响应返回给客户端之后,上下文的许多部分可能已被处置或以其他方式不可用。测试一下,看看会发生什么爆炸。但是,即使现在什么都没有爆炸,您(或其他人)可能稍后会回到该代码,尝试在上下文中使用其他东西,并在它爆炸时变得非常困惑。它可能会导致一些难以调试的场景。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句