如何使ASP.Net MVC控制器动作异步

时间:2015-10-15 15:57:41

标签: asp.net-mvc async-await

我的目标是创建一个MVC控制器Action,它可以异步执行长时间运行的i / o操作。我的目标是避免在这个长时间运行的方法完成时绑定ASP.Net线程池中的线程。

The Action进行了两次调用。

第一个调用是第三方dll,它不包含任何异步方法。这个DLL从专有数据库读取并执行相当复杂的cpu绑定处理。返回可能需要几秒钟的时间。

第二个调用将第一个调用的结果用作使用Entity Framework传递给数据库查询的参数。

简化,这是行动:

public async Task<ActionResult> MyActionAsync(arg1, arg2)
{
    var parameters = 3rdPartyComponent.TakesLongTime(arg1, arg2);

    Task<List<MyClass>> genericList = null;

    using (DbContexts.MyDbContext db = new DbContexts.MyDbContext())
        {
          genericList = await db.Database.SqlQuery<MyClass>(sql,parameters).ToListAsync();
        }

    return View("MyView", genericList);
}

我想打电话给3rdPartyComponent。我最初的想法是这样做:

var parameters = await Task.Run(() => 3rdPartyComponent.TakesLongTime()).ConfigurateAwait(false);

但我已经读过几位主题专家明确表示在asp.net MVC Action中使用Task.Run()会产生反作用,永远不应该这样做。

3rdPartyComponent是一个黑盒子的编译代码,不能改变它来添加异步方法。

有没有办法让对3rdPartyComponent的调用等待,以便整个Action在不占用asp.net线程池中的线程的情况下运行?

2 个答案:

答案 0 :(得分:6)

  

第一个调用是第三方dll,它不包含任何异步方法。这个dll从专有数据库中读取并执行相当复杂的cpu绑定处理。

     

我想打电话给3rdPartyComponent。

     

有没有办法让对3rdPartyComponent的调用等待,以便整个Action在不占用asp.net线程池中的线程的情况下运行?

代码已经尽可能好了。 Task.RunTask.Factory.StartNew都不会给您带来任何好处(即使您传递了LongRunning标志)。

由于第三方dll执行CPU绑定代码,因此需要一个线程。即使您可以修改它,也只能使db访问(I / O工作)异步;根据定义,CPU工作是同步的,从你的描述来看,它听起来像CPU工作的工作是大部分时间。

关于在ASP.NET上避免Task.Run(甚至更糟糕的Task.Factory.StartNew)的全部观点是它们会导致行为效率降低。通过释放ASP.NET请求线程,您只需将线程返回到线程池,而不使用它;关于ASP.NET请求线程,没有什么神奇或特殊的东西。所以Task.Run通过切换到另一个线程池线程来释放一个线程池线程,Task.Factory.StartNewLongRunning通过创建一个全新的线程释放一个线程池线程,将工作安排到该线程,然后在最后拆除该线程(此行为未记录,也未指定,但它是当前观察到的行为)。

所以你最终做的只是招致不必要的线程切换(在StartNew的情况下,是一个完整的额外线程)。 ASP.NET旨在处理同步和异步工作;如果你有同步代码,那么最好的办法就是直接执行它,就像你的代码已经完成一样。

答案 1 :(得分:1)

是的,我认为在异步服务器方法中使用Task.Run是一种反模式,因为你安排的工作最终会在ThreadPool中被重新排队,而这正是你刚刚来到的地方from ... net gain zero(减去在ThreadPool中调度回调的开销)。

我的第一个想法是用

解雇工作
Task.Factory.StartNew(action,TaskCreationOptions.LongRunning)

TaskCreationOptions.LongRunning提示应该在新的Thread而不是ThreadPool中完成工作。

...但是如果这是一种繁重的流量方法,那么你冒着创建线程的速度比他们清理的速度快,现在你已经失去了ThreadPool的管理优势。

如果交通真的像你说的那么高,并且需要这种特殊处理,那么某些节流和/或缓存可能是有序的......但那将是一个不同的问题......