我有一个 .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 会是一个更好的主意和更被接受的方式吗?
例如,控制器是否应该被标记为异步,并且每次调用服务方法时都需要等待?
service
是否不为 getdata
方法提供异步替代方案?此外,通常您应该更喜欢 Task.Run()
而不是创建任务然后手动启动它们。
Task.WaitAll
时,它会有所帮助,因为您正在用该行停放一个线程。
Start
。它们不是线程。只需使用 Task.Run
。然后使用 await Task.WhenAll
这很好用,但是我想知道使用任务是否是最好的解决方案?使用 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 对象。因此,使用异步方法,您可以等待任务而不是阻塞线程。您可以使用 Task
RanToCompletion
的检查。你期待提前取消吗?如果任何调用出错,await Task.WhenAll
将引发异常
据我了解,您希望它并行执行,所以我认为您的代码没有任何问题。正如加布里埃尔所说,您可以等待任务的完成。
[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);
}
不定期副业成功案例分享
await
每个任务。即使在这种情况下,我也更喜欢Task.WhenAll
,因为它使代码更清晰。它也更有效率(只在上下文中恢复一次而不是 3 次),但我的主要原因是代码清晰。Process
只是没有非常适合异步的 API。 :/