LINQ代码中的异步 - 澄清?

时间:2015-07-18 09:11:32

标签: c# .net asynchronous async-await task-parallel-library

几乎每个SO关于这个主题的答案都说明了:

  

LINQ doesn't work perfectly with async

另外:

  

I recommend that you not think of this as "using async within LINQ"

但在斯蒂芬的书中有一个样本:

  

问题:您有一系列要等待的任务,并且您想要做一些事情   完成后处理每个任务。但是,你想做   每个完成后,每个的处理,等待   任何其他任务。

推荐的解决方案之一是:

static async Task<int> DelayAndReturnAsync(int val)
{
 await Task.Delay(TimeSpan.FromSeconds(val));
 return val;
}

// This method now prints "1", "2", and "3".
static async Task ProcessTasksAsync()
{
 // Create a sequence of tasks.
 Task<int> taskA = DelayAndReturnAsync(2);
 Task<int> taskB = DelayAndReturnAsync(3);
 Task<int> taskC = DelayAndReturnAsync(1);
 var tasks = new[] { taskA, taskB, taskC };
 var processingTasks = tasks.Select(async t =>
    {
    var result = await t;
    Trace.WriteLine(result);
    }).ToArray();

// Await all processing to complete
await Task.WhenAll(processingTasks);

}

问题#1:

我不明白为什么现在async在LINQ语句中 - 确实有效。我们不是只是说“考虑在LINQ中使用async吗?”

问题#2:

当控件到达await t时 - 实际发生了什么?控件是否离开ProcessTasksAsync方法?或者它是否保留匿名方法并继续迭代?

3 个答案:

答案 0 :(得分:8)

  

我不明白为什么现在在LINQ语句中异步 - 确实有效。我们不是只说“不考虑在LINQ中使用异步”吗?

async 主要不能与LINQ一起使用,因为IEnumerable<T>扩展名并不总是正确推断委托类型并且遵从Action<T>。他们对Task类没有特别的了解。这意味着实际的异步委托变为async void,这很糟糕。在Enumerable.Select的情况下,我们有一个重载,返回Func<T>(在我们的例子中反过来将是Func<Task>),相当于async Task,因此它适用于异步用例。

  

当控件到达等待时间 - 实际发生了什么?控件是否离开ProcessTasksAsync方法?

不,它没有。 Enumerable.Select是关于预测序列中的所有元素。这意味着对于集合中的每个元素,await t将控制回迭代器,迭代器将继续迭代所有元素。这就是为什么你以后必须await Task.WhenAll,以确保所有元素都已完成执行。

答案 1 :(得分:3)

问题1:

不同之处在于每项任务都是继续,并进行了额外的处理:Trace.WriteLine(result);。在您指向的链接中,该代码不会更改任何内容,只会产生等待和包装其他任务的开销。

问题2:

  

当控件到达等待时间 - 实际发生了什么?

等待ProcessTasksAsync任务的结果,然后继续Trace.WriteLine(result);。我们可以说当我们得到结果时控件会离开ProcessTasksAsync方法,并且处理仍然在匿名方法中。

最后,我们有await Task.WhenAll(processingTasks);等待所有任务,包括在继续之前完成的额外处理(Trace.WriteLine(result);),但每个任务都没有等待其他任务继续执行:Trace.WriteLine(result);

答案 2 :(得分:0)

这样会更好:

static async Task<int> DelayAndReturnAsync(int val)
{
    await Task.Delay(TimeSpan.FromSeconds(val));
    return val;
}
static async Task AwaitAndProcessAsync(Task<int> task)
{
    var result = await task;
    Console.WriteLine(result);
}
// This method now prints "1", "2", and "3".
static async Task ProcessTasksAsync()
{
    // Create a sequence of tasks.
    Task<int> taskA = DelayAndReturnAsync(2);
    Task<int> taskB = DelayAndReturnAsync(3);
    Task<int> taskC = DelayAndReturnAsync(1);
    var tasks = new[] { taskA, taskB, taskC };
    var processingTasks = tasks.Select(AwaitAndProcessAsync).ToArray();
    // Await all processing to complete
    await Task.WhenAll(processingTasks);
}

任务数组,因为AwaitAndProcessAsync返回任务。