我的REST API服务器中具有以下内容(使用OWIN):
[ResponseType(typeof(Session))]
[Route("api/v1/async")]
public Session async_status()
{
AsyncStatus();
return new Session() {...};
}
private async Task<int> AsyncStatus()
{
await SyncMethod();
return 42;
}
private Task<int> SyncMethod()
{
CopyFileThatTakesALongTime(....);
// OR
Thread.Sleep(60000);
return Task.FromResult(42);
}
为什么会死,它会阻塞并且客户端无法获得所需的异步性。我希望在致电时
await SyncMethod();
调用者将被释放和解除阻塞,而其余的睡眠将被执行。我想念什么?
P.S:我可以通过替换以下内容轻松修复它:
AsyncStatus();
使用:
Task.Run(() => AsyncStatus() );
我不这样做的原因是因为阅读:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html
换句话说,如果像上面的文章中那样在Web服务器中使用Task.Run()不好,但是您不能使用async / await,那么解决方案是什么?
答案 0 :(得分:2)
async
方法仅在异步工作开始时变为异步。
也就是说,您的所有代码将同步运行,直到真正异步的启动。一旦开始一些真正的异步工作,就会在调用堆栈中返回Task
,然后可以由调用方法等待(或不等待)。
这样做的含义是,如果您在异步工作之前执行大量同步工作,那么异步方法仍然可以阻止请求。您的示例完美地说明了这一点。让我们确切地分解一个线程在做什么:
async_status()
AsyncStatus()
await SyncMethod()
,线程跳转到SyncMethod()
。Thread.Sleep(60000)
处暂停60秒。SyncMethod()
返回完整的Task
。AsyncStatus()
同步继续并返回一个值。async_status()
返回并因此返回请求。 Thread.Sleep(60000)
可以替换为任何处理器密集型任务(或任何长时间运行的非异步任务),并且如果发生在该方法的第一个await
之前,结果将是相同的。
这在Microsoft文档中的标题What happens in an async method下也有描述。注意点3:
GetStringAsync
中发生了某事,这会暂停其进度。也许它必须等待网站下载或其他阻止活动。为了避免阻塞资源,GetStringAsync
将控制权交给其调用者AccessTheWebAsync
。
控制仅在返回Task
时返回到调用方法 ,这仅在异步任务实际启动时发生。在您的示例中,没有任何异步任务发生。如果您添加一个(甚至只是await Task.Delay(1)
),则会看到不同的结果。例如,这将立即返回:
[ResponseType(typeof(Session))]
[Route("api/v1/async")]
public Session async_status()
{
AsyncStatus();
return new Session() {...};
}
private async Task<int> AsyncStatus()
{
await SyncMethod();
return 42;
}
private async Task<int> SyncMethod()
{
await Task.Delay(1);
Thread.Sleep(60000);
return 42;
}
在await Task.Delay(1)
(或任何真正异步工作):
SyncMethod()
向Task
返回不完整的AsyncStatus()
AsyncStatus()
向Task
返回不完整的async_status()
。Task
,所以继续执行async_status()
要回答您的问题:
您问:
如果像上面的文章中那样在Web服务器中使用Task.Run()不好,但是您不能使用async / await,那么解决方案是什么?
您提到了Task.Run Etiquette文章。最后阅读他的结论:
请勿仅将其用于“为我的异步方法提供可使用的内容”。
您在这里的目的不仅是“提供可等待的东西”,而且是开始进行后台作业,而不是让客户端等到完成为止。不一样。
是的,您实际上可以使用Task.Run()
。但是,正如Stephen Cleary的Fire and Forget on ASP.NET文章所述,这样做的问题在于ASP.NET无法跟踪该后台任务。因此,如果长时间运行,则回收应用程序池可能会立即终止该工作。他的文章描述了执行后台工作的更好方法,这些方法您不想阻止请求的返回。