在c#中并行异步加载多个变量的最优雅方法是什么?

时间:2017-09-15 08:34:24

标签: c# async-await

最好用一个例子来说明。

假设我想在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;

有没有更好,更简洁的方法来实现这一目标?

2 个答案:

答案 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));