我有一个.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);
}
这很好用,但是我想知道使用任务是否是这里的最佳解决方案?使用 async/await 会是一个更好的主意和更被接受的方式吗?
例如,是否应该将控制器标记为异步,并在每次调用服务方法时放置一个等待?
这很好用,但是我想知道使用任务是否是这里的最佳解决方案?使用 async/await 会是一个更好的主意和更被接受的方式吗?
是的,绝对。对 ASP.NET 执行并行处理会占用每个请求的多个线程,这会严重影响可伸缩性。异步处理在 I/O 方面要优越得多。
若要使用 async
,首先从服务中某个位置的最低级别调用开始。它可能在某个时候进行 HTTP 调用;将其更改为使用异步 HTTP 调用(例如,HttpClient
)。然后让async
从那里自然生长。
最终,您将得到异步getdata1Async
、getdata2Async
和 getdata3Async
方法,这些方法可以并发使用:
[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);
}
- 等待任务时,操作线程当前被阻止。使用
TaskWhenAll
返回可等待的任务对象。因此,使用异步方法,您可以等待任务而不是阻塞线程。 - 您可以使用
Task<T>
返回所需类型的结果,而不是创建局部变量并在任务中分配它们。 - 使用
Task<TResult>.Run
方法,而不是创建和运行任务 - 我建议对操作名称使用约定 - 如果操作接受 GET 请求,它的名称应以
Get
开头 - 接下来,您应该检查任务是否成功完成。这是通过检查任务状态来完成的。在我的示例中,如果某些任务未成功完成,我将返回对象属性
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);
}