在web api控制器中使用async/await或task(.net core)

Ben*_*ron 43 .net c# asp.net-web-api asp.net-core

我有一个.net核心API,它有一个控制器,用于构建要返回的聚合对象.它创建的对象由来自对服务类的3个方法调用的数据组成.它们彼此独立,可以彼此隔离运行.目前我正在使用任务来提高此控制器的性能.当前版本看起来像这样......

[HttpGet]
public IActionResult myControllerAction()
{      
    var data1 = new sometype1();
    var data2 = new sometype2();
    var data3 = new List<sometype3>();

    var t1 = new Task(() => { data1 = service.getdata1(); });
    t1.Start();

    var t2 = new Task(() => { data2 = service.getdata2(); });
    t2.Start();

    var t3 = new Task(() => { data3 = service.getdata2(); });
    t3.Start();

    Task.WaitAll(t1, t2, t3);

    var data = new returnObject
    {
         d1 = data1,
         d2 = data2,
         d2 = data3
    };

    return Ok(data);
}
Run Code Online (Sandbox Code Playgroud)

这很好用,但我想知道在这里使用任务是否是最佳解决方案?使用async/await是一个更好的主意和更容易接受的方式吗?

例如,控制器是否应标记为异步,并且每次调用服务方法时都要等待?

Ste*_*ary 65

这很好用,但我想知道在这里使用任务是否是最佳解决方案?使用async/await是一个更好的主意和更容易接受的方式吗?

是的,一点没错.在ASP.NET上执行并行处理会占用每个请求多个线程,这会严重影响您的可伸缩性.异步处理在I/O方面要优越得多.

要使用async,首先从您的最低级别电话开始,在服务中的某个地方.它可能在某个时候进行HTTP调用; 改变它以使用异步HTTP调用(例如,HttpClient).然后让async自然从那里成长.

最终,您将最终得到异步getdata1Async,getdata2Asyncgetdata3Async方法,这些方法可以同时使用:

[HttpGet]
public async Task<IActionResult> myControllerAction()
{
  var t1 = service.getdata1Async();
  var t2 = service.getdata2Async();
  var t3 = service.getdata3Async();
  await Task.WhenAll(t1, t2, t3);

  var data = new returnObject
  {
    d1 = await t1,
    d2 = await t2,
    d3 = await t3
  };

  return Ok(data);
}
Run Code Online (Sandbox Code Playgroud)

使用此方法,在三个服务调用正在进行时,myControllerAction使用线程而不是四个.

  • 如果返回类型不同,则必须"等待"每个任务.即使在这种情况下,我更喜欢`Task.WhenAll`因为它使代码更清晰.它也更高效(仅在上下文中恢复一次而不是3次),但我的主要原因是代码清晰度. (8认同)
  • 对于返回对象中的每个任务,等待Task.WhenAll()*和*等待是不是多余的?不确定为什么做一个或另一个不够.你能解释一下吗?:) (4认同)

Ser*_*kiy 12

[HttpGet]
public async Task<IActionResult> GetAsync()
{      
    var t1 = Task.Run(() => service.getdata1());
    var t2 = Task.Run(() => service.getdata2());
    var t3 = Task.Run(() => service.getdata3());

    await Task.WhenAll(t1, t2, t3);

    var data = new returnObject
    {
        d1 = t1.Status == TaskStatus.RanToCompletion ? t1.Result : null,
        d2 = t2.Status == TaskStatus.RanToCompletion ? t2.Result : null,
        d3 = t3.Status == TaskStatus.RanToCompletion ? t3.Result : null
    };

   return Ok(data);
}
Run Code Online (Sandbox Code Playgroud)
  1. 当您等待任务时,您的操作线程当前被阻止.使用TaskWhenAll返回awaitable任务对象.因此,使用异步方法,您可以等待任务而不是阻塞线程.
  2. 您可以使用Task<T>返回所需类型的结果,而不是创建局部变量并在任务中分配它们.
  3. 使用Task<TResult>.Run方法而不是创建和运行任务
  4. 我建议使用约定作为动作名称 - 如果动作接受GET请求,它的名称应该以 Get
  5. 接下来,您应该检查任务是否成功完成.它通过检查任务状态来完成.在我的示例中null,如果某些任务未成功完成,我会使用返回对象属性的值.您可以使用其他方法 - 例如,如果某些任务失败,则返回错误.

  • 我想你需要解释一下`RanToCompletion`的检查.你期待提早取消吗?如果任何调用出现故障,`等待Task.WhenAll`会引发异常 (4认同)