Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# webapi中的异步调用_C#_Asynchronous_Asp.net Web Api - Fatal编程技术网

C# webapi中的异步调用

C# webapi中的异步调用,c#,asynchronous,asp.net-web-api,C#,Asynchronous,Asp.net Web Api,在asp.net Web API中,如何异步调用长时间运行的阻塞操作 我有一个web api控制器操作,它需要进行一个相对长(例如10秒)的运行DB调用。这似乎是异步方法的候选方法。我可以将长时间运行的任务卸载到新线程上,并取消阻止我的asp.net请求线程以处理其他一些请求,因此我简化的控制器操作如下所示: public async Task<IHttpActionResult> Get() { IEnumerable<Thing> things = await

在asp.net Web API中,如何异步调用长时间运行的阻塞操作

我有一个web api控制器操作,它需要进行一个相对长(例如10秒)的运行DB调用。这似乎是异步方法的候选方法。我可以将长时间运行的任务卸载到新线程上,并取消阻止我的asp.net请求线程以处理其他一些请求,因此我简化的控制器操作如下所示:

public async Task<IHttpActionResult> Get()
{
    IEnumerable<Thing> things = await Task.Run(() => DoLongDbCall());
    return Ok(things);
}
公共异步任务Get() { IEnumerable things=wait Task.Run(()=>DoLongDbCall()); 返回Ok(事物); } 我遇到过一些博客(,例如)认为这可能不是在asp.net中实现这一点的最佳方式。作者建议使用
Task.FromResult()
并同步执行DB调用,但我看不出这有什么帮助;我的请求线程仍将被阻止,等待DB调用返回

我的请求线程仍将被阻止,等待DB调用返回

这并不完全正确:

您的请求仍将等待长时间操作结束,但您的线程将可以自由处理其他操作

您必须记住,IIS的可用线程数量减少(特别是在非服务器系统下运行时),释放一个不需要的线程总是一件好事


此外,如果线程短缺,如果使用<代码>任务。运行< /COD>,异步操作将等待可用线程,如果不释放当前线程,则最终会出现一个可怕的死锁。

首先,考虑同步调用时发生的情况:

public IHttpActionResult Get()
{
  IEnumerable<Thing> things = DoLongDbCall();
  return Ok(things);
}
一个请求进入,ASP.NET抓取一个线程池线程来处理该请求。该线程调用
Get
方法,然后该方法抓取另一个线程池线程来执行工作,并将原始线程池线程返回到线程池。一个线程池线程在整个请求期间使用(两个线程池线程在非常短的时间内使用)

因此,
Task.Run
代码强制执行额外的线程转换,而不提供任何好处(在服务器端使用
async
的全部目的是释放线程)。这就是为什么我建议不要在ASP.NET上使用
Task.Run
(或以任何其他方式在线程池上运行工作)

适当的异步解决方案是使用异步DB调用:

public async Task<IHttpActionResult> Get()
{
  IEnumerable<Thing> things = await DoLongDbCallAsync();
  return Ok(things);
}
公共异步任务Get() { IEnumerable things=wait DoLongDbCallAsync(); 返回Ok(事物); }
一个请求进入,ASP.NET抓取一个线程池线程来处理该请求。该线程调用
Get
方法,该方法随后启动异步操作并将线程池线程返回到线程池。稍后,当db调用完成时,ASP.NET会抓取一个线程池线程来完成请求。对于大多数请求,不使用线程池线程(一个线程池线程在请求开始和结束时的一段短时间内使用)。

所有请求都已在单独的线程上执行,因此,除非您希望并行执行两个或多个调用,否则不需要考虑阻塞。调用
await Task.Run
只会在可以使用原始线程的情况下浪费另一个线程。您意识到在这种情况下,使用
async
/
await
真的没有什么好处吗?ADO.NET和EF确实支持异步调用
DoLongDbCall
本身应该是一个异步方法。在这种情况下,您将在等待数据库时释放当前线程,而不使用额外的线程等待,这在某种程度上是有意义的,但我看不出
Task.Run(()=>LongAction)
LongActionAsync
之间的区别。不管我在它上面放了多少层抽象,我的“异步”在某个时候都需要创建一个新线程?@richzilla:不,它不一定需要单独的线程。我在我的博客上解释。这解释得太好了,我要尽快奖励一笔赏金。谢谢你,伙计。
public async Task<IHttpActionResult> Get()
{
  IEnumerable<Thing> things = await DoLongDbCallAsync();
  return Ok(things);
}