我需要实现启动处理开始的服务。但是我不需要等待结果。我只能显示默认输出,并且在后台运行该过程。
但是我想到了一个问题,即等待后的代码没有执行。
我准备了一些代码来展示这个想法:
public class HomeController : Controller
{
public ActionResult Deadlock()
{
AsyncCall();
return View();
}
private async Task AsyncCall()
{
await Task.Delay(10000);
var nonreachablePlace = "The breakpoint will not set the execution here";
Do(nonreachablePlace);
}
private void Do(string m)
{
m.Contains("x");
}
}
我知道它看起来非常糟糕。但是这个主意是这样的:到我的坏6步没有处理。我试图设置断点,但从未被击中。
但是,如果我减少时间延迟并在调试中进行,则将进入第6步。
Enter to the controller's action method
After return from controller's action method
但是,如果我在等待后仅留下一个断点,则不会执行6步
var nonreachablePlace = "The breakpoint will not set the execution here";
笔记:我将
ConfigureAwait(false)
附加到Task.Delay()
。看起来像这样:private async Task AsyncCall()
{
await Task.Delay(10000).ConfigureAwait(false);
var nonreachablePlace = "The breakpoint will not set the execution here";
Do(nonreachablePlace);
}
现在,它可以按预期运行。我的问题是,如果没有ConfigureAwait(false),为什么代码不起作用?
也许它与
SynchronizationContext
有关,并且在主线程完成其工作之后无法访问。然后在等待的方法尝试获取已经处理过的上下文之后(只是我的想法)
最佳答案
使用HostingEnvironment.QueueBackgroundWorkItem
。
请注意,这仅在.NET Framework上的经典ASP.NET(System.Web.dll
)中可用,而在ASP.NET Core中不可用(我忘记了它在.NET Framework上运行的ASP.NET Core 1.x和2.x中能在多大程度上起作用,但是反正)。
您需要的是:
using System.Web.Hosting;
public class MyController : Controller
{
[HttpPost( "/foo" )]
public async Task<ActionResult> DoSomething()
{
HostingEnvironment.QueueBackgroundWorkItem( this.DoSomethingExpensiveAsync ); // Pass the method by name or as a `Func<CancellationToken,Task>` delegate.
return this.View();
}
private async Task DoSomethingExpensiveAsync( CancellationToken cancellationToken )
{
await Task.Delay( TimeSpan.FromSeconds( 30 ) );
}
}
您还可以将其用于非异步工作负载: [HttpPost( "/foo" )]
public async Task<ActionResult> DoSomething()
{
HostingEnvironment.QueueBackgroundWorkItem( this.DoSomethingExpensive ); // Pass the method by name or as a `Action<CancellationToken>` delegate.
return this.View();
}
private void DoSomethingExpensive( CancellationToken cancellationToken )
{
Thread.Sleep( 30 * 1000 ); // NEVER EVER EVER call Thread.Sleep in ASP.NET!!! This is just an example!
}
如果您想一开始就正常地开始工作,并且如果花费的时间太长,则只能在后台完成它,那么请执行以下操作: [HttpPost( "/foo" )]
public async Task<ActionResult> DoSomething()
{
Task<String> workTask = this.DoSomethingExpensiveAsync( default );
Task timeoutTask = Task.Delay( TimeSpan.FromSeconds( 5 ) );
Task first = await Task.WhenAny( workTask, timeoutTask );
if( first == timeoutTask )
{
// `workTask` is still running, so resume it in the background:
HostingEnvironment.QueueBackgroundWorkItem( async ct => await workTask );
return this.View( "Still working..." );
}
else
{
// `workTask` finished before the timeout:
String result = await workTask; // or just `workTask.Result`.
return this.View( result );
}
}
private async Task<String> DoSomethingExpensiveAsync( CancellationToken cancellationToken )
{
await Task.Delay( TimeSpan.FromSeconds( 30 ) );
return "Explosive bolts, ten thousand volts; At a million miles an hour, Abrasive wheels and molten metals";
}
关于c# - 从同步操作方法中调用带有await的异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65587910/