Asp.net mvc 如何使ASP.NETMVC控制器操作异步

Asp.net mvc 如何使ASP.NETMVC控制器操作异步,asp.net-mvc,async-await,Asp.net Mvc,Async Await,我的愿望是使MVC控制器动作能够异步执行长时间运行的i/o操作。我的目标是在这个长时间运行的方法完成时避免在ASP.Net线程池中占用线程 该行动发出两个呼吁 第一个调用是对不包含任何异步方法的第三方dll的调用。此dll从专有数据库读取数据,并执行相当复杂的cpu绑定处理。返回可能需要几秒钟的时间 第二个调用使用第一个调用的结果作为参数,这些参数使用实体框架传递给数据库查询 简而言之,这就是行动: public async Task<ActionResult> MyActionAs

我的愿望是使MVC控制器动作能够异步执行长时间运行的i/o操作。我的目标是在这个长时间运行的方法完成时避免在ASP.Net线程池中占用线程

该行动发出两个呼吁

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

第二个调用使用第一个调用的结果作为参数,这些参数使用实体框架传递给数据库查询

简而言之,这就是行动:

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);
}
但我读过一些主题专家明确指出,在asp.net MVC操作中使用Task.Run()会适得其反,永远不应该这样做

3rdPartyComponent是一个编译代码的黑盒,不能修改为添加异步方法


有没有办法让对3rdPartyComponent的调用可以等待,这样整个操作运行时就不会占用asp.net线程池中的线程?

是的,我认为在异步服务器方法中使用
Task.Run
是一种反模式,因为您安排的工作最终只会在线程池中重新被调用,那正是你刚来的地方。。。净增益为零(减去线程池中调度回调的开销)

我的第一个想法是用它来完成这项工作

Task.Factory.StartNew(action,TaskCreationOptions.LongRunning)
TaskCreationOptions.LongRunning
是一个提示,提示工作应该在新线程而不是线程池中完成

…但是如果这是一种高流量的方法,那么创建线程的速度可能会比清除线程的速度快,而现在您已经失去了线程池的管理优势

如果流量真的如你所说的那样高,并且需要这种特殊处理,某种节流和/或缓存可能是合适的。。。但那将是一个不同的问题

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

我想打电话给第三方组件,请稍候

是否有任何方法可以使对3rdPartyComponent的调用处于等待状态,以便整个操作在运行时不会占用asp.net线程池中的线程

代码已经达到了它所能达到的程度。无论是
Task.Run
还是
Task.Factory.StartNew
都不会给您带来任何好处(即使您通过了
longtrunning
标志)

由于第三方dll不执行CPU绑定的代码,因此它需要一个线程。即使可以修改,也只能使db访问(I/O工作)异步;CPU工作从定义上讲是同步的,从您的描述来看,CPU工作在大多数情况下都是固定的

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


因此,您最终要做的就是产生不必要的线程切换(在
StartNew
的情况下,是一个完整的额外线程)。ASP.NET设计用于处理同步和异步工作;如果您有同步代码,最好直接执行它,就像您的代码已经在执行一样。

Task.Run()ASP.NET MVC内部的行为是反作用的,不应该这样做。这是不正确的。有很多情况下你可能想这样做。我不是说你应该一直这样做,但是如果你想产生一个分离的线程,那么为什么不呢?嘿,汤姆,读一读这个:并且考虑你是否在你的O中过早。优化…除非这是一种高流量的方法,否则这当然可能是一个不必要的步骤。(您可能最终使用Task.Factory.StartNew,以便向提供TaskCreationOptions,以便安排线程池外的工作)@spender,是的,这是一种流量非常大的方法。在这种情况下,使用线程是一个坏主意。但这并不意味着永远不应该这样做。web应用程序中的线程应该用于同时执行多个任务,或者作为启动和忘记线程。生成一个线程来完成主进程可以完成的相同工作(当主线程等待时)会适得其反。你在同一个任务中使用了两倍多的线程。我相信这是他的观点。缓存
3rdPartyComponent.TakesLongTime()
?…是的,你真的可以像那样拼写“caching”!我在谷歌上搜索“caching”,它告诉我“显示缓存结果”:+1用于TaskCreationOptions.LongRunning提示。但我认为这个问题的真正答案是“不要尝试使此操作异步,它将产生比修复更多的问题。”
Task.Factory.StartNew(action,TaskCreationOptions.LongRunning)