在Web API中使用async和许多异步调用

时间:2015-03-30 15:39:09

标签: asp.net asp.net-web-api async-await

对于求职者的问题,我很抱歉,我在网上和网上看了很多帖子,还有一些我无法理解的重要内容。

据我了解,WebAPI中异步操作的使用主要是出于可伸缩性原因,因此任何传入请求都将转移到工作者而不是线程,并且可以提供更多请求。

我的项目包括几次由EF6多次从DB读取/插入/更新的大型操作。这个动作看起来像是:

    public async Task<HttpResponseMessage> Get(int id)
    {
     PolicyModel response = await policyRepository.GetPolicyAsync(id);
     return Request.CreateResponse<PolicyModel>(HttpStatusCode.OK,response);
    }

和GetPolicyAsync(int id)看起来像这样:

    public async Task<PolicyModel> GetPolicyAsync(int id)
    {
      PolicyModel response = new PolicyModel();
      User currentUser = await Repositories.User.GetUserDetailsAsync(id);

      if(currentUser.IsNew)
      {
         IEnumerable<Delivery> deliveries = await Repositories.Delivery.GetAvailableDeliveries();
         if(deliveries == null || deliveries.Count() == 0
         {
           throw new Exception("no deliveries available");
         }
         response.Deliveries = deliveries;
         Ienumerable<Line> lines = await Repositores.Delivery.GetActiveLinesAsync();
         lines.AsParallel().ForAll(line => {
         await Repositories.Delivery.AssignLineAsync(line,currentUser);
    }
    ...
      return response;   
    }

我没有编写完整的代码,但是它有很多并且它也被分解为几种方法,但这就是它的精神

现在我的问题是:在一种方法中使用这么多的等待者是一种好习惯吗?我看到调试起来比较困难,难以维护线程上下文并且为了工作者分配,我不应该只使用Task.Factory.StartNew()或者调用简单的Task.Delay()以便请求将被立即转移给工人? 我知道这不是一个好习惯(一直异步)所以也许只是在GetpolicyAsync(int id)方法结束/开始时的一个异步方法

编辑:

因为我理解.net中异步方法的机制,对于每个异步方法,编译器都在寻找一个免费线程并让它处理该方法,线程正在寻找一个自由工作者并将方法分配给它然后向编译器报告它是免费的。因此,如果我们有10个线程,并且每个线程有10个工作线程,则该程序可以处理100个并发异步方法。 所以回到web开发,IIS将x线程分配给每个应用程序池,例如10。这意味着异步WebAPI方法可以处理100个请求,但如果内部有另一个异步方法,可以使用的请求数量为50,依此类推,我是对的吗? 并且据我所知,我必须调用异步方法才能使WebAPI方法真正异步,现在,因为使用Task.Factory.StartNew()是一种不好的做法,我必须至少使用Task.Delay()

我真正想要获得的是异步WebAPI方法的可伸缩性和同步方法的上下文感知

在我到目前为止看到的所有示例中,它们只显示了一个非常简单的代码,但在现实生活中,方法要复杂得多

由于

2 个答案:

答案 0 :(得分:2)

在单个方法中使用多个await并没有错。如果方法对你来说太复杂了,你可以将它分成几种方法:

public async Task<PolicyModel> GetPolicyAsync(int id)
{
    PolicyModel response = new PolicyModel();
    User currentUser = await Repositories.User.GetUserDetailsAsync(id);

    if(currentUser.IsNew)
    {
        await HandleDeliveriesAsync(await Repositories.Delivery.GetAvailableDeliveries());
    }
    ...
    return response;   
}

public async Task HandleDeliveriesAsync(IEnumerable<Delivery> deliveries)
{
    if(deliveries == null || deliveries.Count() == 0
    {
        throw new Exception("no deliveries available");
    }
    response.Deliveries = deliveries;
    Ienumerable<Line> lines = await Repositores.Delivery.GetActiveLinesAsync();
    lines.AsParallel().ForAll(line => {
    await Repositories.Delivery.AssignLineAsync(line,currentUser);   
}

不要使用Task.Factory.StartNewTask.Delay因为它只是将相同的工作卸载到不同的线程。它在大多数情况下都没有增加任何价值,并且在ASP.Net中实际上有害。

答案 1 :(得分:0)

经过多次搜索,我发现了这个艺术: https://msdn.microsoft.com/en-us/magazine/dn802603.aspx

它解释了@ i3arnon在评论中所说的内容。根本没有工人,线程正在做所有的工作。
简而言之,处理Web请求的线程会到达一个设计为在驱动程序堆栈上异步完成的操作,它会创建一个请求并将其传递给驱动程序。
驱动程序将其标记为挂起,并向线程报告“完成”,该线程返回线程池以获得另一个分配。
实际工作不是由线程完成的,而是由所有线程借用cpu时间并让他们自由地参与他们的业务的驱动程序。
当完成操作时,驱动程序会通知它并且可用的线程继续...

所以,从中我了解到我应该研究每一个异步方法并检查它是否实际上做了一个非同步使用驱动程序的操作。

要点思考:您网站的可扩展性取决于真正异步完成的操作量,如果您没有任何一个,那么您的网站将无法扩展。

主持人,我是一个真正的新手,如果我弄错了,请纠正我 谢谢!