C# 使控制器异步的最简单方法

C# 使控制器异步的最简单方法,c#,.net,asp.net-mvc-5,async-await,C#,.net,Asp.net Mvc 5,Async Await,我继承了一个使用MVC5和C的大型web应用程序。我们的一些控制器会进行几次缓慢的数据库调用,我想让它们异步,以便允许工作线程在等待数据库调用完成时为其他请求提供服务。我想用最少的重构来实现这一点。假设我有以下控制器 public string JsonData() { var a = this.servicelayer.getA(); var b = this.servicelayer.getB(); return SerializeObject(new {a, b}); }

我继承了一个使用MVC5和C的大型web应用程序。我们的一些控制器会进行几次缓慢的数据库调用,我想让它们异步,以便允许工作线程在等待数据库调用完成时为其他请求提供服务。我想用最少的重构来实现这一点。假设我有以下控制器

public string JsonData()
{
   var a = this.servicelayer.getA();
   var b = this.servicelayer.getB();
   return SerializeObject(new {a, b});
}
通过保持服务层不变并将控制器重写为

public async Task<string> JsonData()
{
   var task1 = Task<something>.Run(() => this.servicelayer.getA());
   var task2 = Task<somethingelse>.Run(() => this.servicelayer.getB());
   await Task.WhenAll(task1, task2);
   var a = await task1;
   var b = await task2;
   return SerializeObject(new {a, b});
}
public异步任务JsonData()
{
var task1=Task.Run(()=>this.servicelayer.getA());
var task2=Task.Run(()=>this.servicelayer.getB());
等待任务。WhenAll(任务1,任务2);
var a=等待任务1;
var b=等待任务2;
返回序列化对象(新的{a,b});
}

上面的代码运行时没有任何问题,但我无法判断使用Visual Studio时工作线程是否现在可用于服务其他请求,或者在asp.net控制器中使用Task.Run()是否与我认为的不一样。有人能对我的代码的正确性发表评论吗?是否可以以任何方式对其进行改进?此外,我还了解到,在控制器中使用async会带来额外的开销,应该只用于长时间运行的代码。我可以使用什么最低标准来决定控制器是否需要异步?我知道每个用例都是不同的,但我想知道是否有一个基线可以作为起点。2个数据库调用?还有超过2秒的时间返回吗

新代码的唯一改进是它同时运行A和B,而不是一次运行一个。这段代码中实际上没有真正的异步

当您使用
Task.Run
时,您正在卸载要在另一个线程上完成的工作,因此基本上您启动两个线程并释放当前线程,同时等待两个任务(每个任务完全同步运行)

这意味着操作将更快地完成(因为并行性),但将使用两倍的线程,因此可伸缩性较差


您要做的是确保所有操作都是真正异步的。这意味着拥有一个
servicelayer.getAAsync()
servicelayer.getBAsync()
,这样您就可以在处理IO时真正释放线程:

public async Task<string> JsonData()
{
    return SerializeObject(new {await servicelayer.getAAsync(), await servicelayer.getBAsync()});
}
public异步任务JsonData()
{
返回序列化对象(新的{await servicelayer.getAAsync(),await servicelayer.getBAsync()});
}
如果您不能确保您的实际IO操作是真正异步的,那么最好保留旧代码


关于为什么要避免
任务的更多信息。运行

指南是,无论何时您有I/O,即数据库,都应该使用异步。与任何类型的I/O相比,开销都很小

也就是说,通过
Task.Run
阻塞线程池线程就是我所说的“伪异步”。这正是你不想在ASP.NET上做的事情


相反,从“最低级别”代码开始,并使其真正异步。例如,EF6支持异步数据库查询。然后让
async
代码从那里自然增长到您的控制器。

@l3arnon-我的问题的两个答案都很有帮助,我发现您发布的链接实际上是由Stephen Cleary编写的。我最终选择了这个答案,因为堆栈溢出只允许您选择一个答案,而您的答案是第一个。非常感谢所有回应的人。