提问者:小点点

在web api控制器(。NET core)中使用Async/Await或task


我有一个。NET core 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);
}

这工作得很好,但是我想知道使用任务是不是最好的解决方案?使用Async/Await会是一个更好的想法和更被接受的方式吗?

例如,是否应该将控制器标记为异步,并在每次调用服务方法时放置一个等待?


共3个答案

匿名用户

这工作得很好,但是我想知道使用任务是不是最好的解决方案?使用Async/Await会是一个更好的想法和更被接受的方式吗?

是的,绝对是。在ASP.NET上执行并行处理会消耗每个请求的多个线程,这会严重影响您的可伸缩性。异步处理对于I/O来说要优越得多。

要使用async,首先从最低级别的调用开始,即服务内部的某个地方。它可能在某个时间点执行HTTP调用;将其更改为使用异步HTTP调用(例如HttpClient)。然后让async从那里自然增长。

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

[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);
}

使用这种方法,当三个服务调用正在进行时,MyControllerAction使用零线程而不是四个线程。

匿名用户

[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);
}
  1. 当您正在等待任务时,您的操作线程当前被阻止。使用TaskWhenAll返回可等待的任务对象。因此,使用异步方法,您可以等待任务,而不是阻塞线程。
  2. 您可以使用task返回所需类型的结果,而不是创建局部变量并在任务中分配它们。
  3. 使用task.run方法
  4. 而不是创建和运行任务
  5. 我建议对操作名称使用约定-如果操作接受GET请求,它的名称应该以GET
  6. 开头
  7. 接下来,您应该检查任务是否成功完成。它是通过检查任务状态来完成的。在我的示例中,如果某些任务没有成功完成,我使用null值返回对象属性。您可以使用另一种方法-例如,如果某些任务失败,则返回错误。

匿名用户

正如我所理解的,您希望这是并行执行的,所以我不认为您的代码有什么问题。正如加布里埃尔提到的,你可以等待任务的完成。

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

  var t1 = Task.Run(() => { data1 = service.getdata1(); });
  var t2 = Task.Run(() => { data2 = service.getdata2(); });
  var t3 = Task.Run(() => { data3 = service.getdata3(); });

  await Task.WhenAll(t1, t2, t3); // otherwise a thread will be blocked here

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

 return Ok(data);
}

您还可以使用任务的结果来保存某些代码行,并使代码整体“更好”(请参阅注释):

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

  await Task.WhenAll(t1, t2, t3); // otherwise a thread will be blocked here

  var data = new returnObject
  {
      d1 = t1.Result,
      d2 = t2.Result,
      d2 = t3.Result
  };

 return Ok(data);
}