最好用一个例子来说明。
假设我想在Web请求开始时从数据库加载多个记录。我想异步地提取所有必要的数据。一世 可能会有这样的事情:
var user = await Database.GetUser(userId);
var group = await Database.GetGroup(groupId);
var members = await Database.GetGroupMembers(groupId);
现在假设我要并行加载这些数据。突然间,这个漂亮的清晰/干净的异步代码变得有点乱。
var userTask = Database.GetUser(userId);
var groupTask = Database.GetGroup(groupId);
var membersTask = Database.GetGroupMembers(groupId);
await Task.WhenAll(userTask, groupTask, membersTask);
var user = userTask.Result;
var group = groupTask.Result;
var members = membersTask.Result;
有没有更好,更简洁的方法来实现这一目标?
答案 0 :(得分:8)
分隔任务开始和[2] Run: 0
[2] Run: 0
[2] Run: 0
Completed work for 2.0
Started work for 0.1
Started work for 0.2 <-- surprise!
[0] Run: 2
[0] Run: 2
[0] Run: 2
[0] Run: 2
[0] Run: 2
:
await
答案 1 :(得分:0)
在我看来,代码看起来很好,除了它should use await
instead of Result
(用于更好的错误处理语义)。
var userTask = Database.GetUser(userId);
var groupTask = Database.GetGroup(groupId);
var membersTask = Database.GetGroupMembers(groupId);
await Task.WhenAll(userTask, groupTask, membersTask);
var user = await userTask;
var group = await groupTask;
var members = await membersTask;
然而,现在元组已经出局,它可以变得更好一些。本周我一直playing around with a tuple-based WhenAll
。现在定义它是一种痛苦(基于反射的方法会更加动态):
public static class AsyncTupleHelpers
{
public static async Task<(T1, T2)> WhenAll<T1, T2>(Task<T1> task1, Task<T2> task2) =>
(await task1.ConfigureAwait(false), await task2.ConfigureAwait(false));
public static async Task<(T1, T2, T3)> WhenAll<T1, T2, T3>(Task<T1> task1, Task<T2> task2, Task<T3> task3) =>
(await task1.ConfigureAwait(false), await task2.ConfigureAwait(false), await task3.ConfigureAwait(false));
/* More if you want, following the same pattern */
}
但一旦定义,就可以这样使用:
var (user, group, members) = await AsyncTupleHelpers.WhenAll(
Database.GetUser(userId),
Database.GetGroup(groupId),
Database.GetGroupMembers(groupId));
不需要单独的任务。
<强>更新强>:
我在原始示例中没有提到它,但是如果找不到记录,我会引发异常。
在这种情况下,您可以使用本地方法:
async Task<User> GetUser(T userId) => await Database.GetUser(userId) ?? throw new Exception("User not found.");
async Task<Group> GetGroup(T groupId) => await Database.GetGroup(groupId) ?? throw new Exception("Group not found.");
var (user, group, members) = await AsyncTupleHelpers.WhenAll(
GetUser(userId),
GetGroup(groupId),
Database.GetGroupMembers(groupId));