Task.WhenAll什么时候枚举?

时间:2017-05-03 13:31:54

标签: linq asynchronous task

我对异步编程很陌生。我想在等待任何任务之前启动一系列任务(发出http请求)。

List<Guid> identifiers;

//Set identifiers to what they should be

var task = Task.WhenAll(identifiers.Select(id => _serviceConnector.GetAsync(id)));

// Call and await another request

await task;

我的问题是:我的http请求是否会通过Task.WhenAll创建任务来启动?或者他们是否会在等待进一步下来之前开始?谢谢!

2 个答案:

答案 0 :(得分:2)

WhenAll立即(并同步)确定其可枚举参数。因此,所有任务都将在WhenAll返回时启动。

如果你想到这一点,那就有道理了。 WhenAll 必须知道等待的任务数量,以便知道自己的任务何时完成。此外,它必须链接到每个任务,以便在每个子任务完成时通知它。没有其他时间做这项工作; 必须计算并在返回之前设置通知。

答案 1 :(得分:2)

  

我的http请求是否会在创建任务时启动   通过Task.WhenAll?或者在等待进一步下降之前它们是否会被启动?

当您将IEnumerable<Task>传递给Task.WhenAll时,它会枚举任务序列并将它们全部列入清单以供进一步处理:

if (tasks == null) throw new ArgumentNullException("tasks");
List<Task<TResult>> taskList = new List<Task<TResult>>();
foreach (Task<TResult> task in tasks)
{
    if (task == null) throw new ArgumentException("tasks");
    taskList.Add(task);
}

这意味着如果您传递LINQ查询,它将被执行:

identifiers.Select(id => _serviceConnector.GetAsync(id))

但这并不意味着Task.WhenAll 启动这些任务。例如。如果您的查询将返回尚未启动的任务,那么这些任务将保持非运行状态。例如。以下查询将创建任务但不会启动它们,因此WhenAll将等待这些任务

var tasks = Enumerable.Range(1, 10).Select(i => new Task<int>(() => i));
var task = Task.WhenAll(tasks);

在您的情况下,一切都取决于GetAsync(id)方法。如果它创建并启动任务(如HttpClient那么做),那么所有任务都将在Task.WhenAll调用开始时创建并启动。

TL; DR Task.WhenAll方法的实施细节。如上所述,它抓取所有给定的任务(对于IEnumerable参数,它将所有任务放入列表中)并创建类型为WhenAllPromise<T>的新任务

private sealed class WhenAllPromise<T> : Task<T[]>, ITaskCompletionAction

正如您所看到的,此任务实现了ITaskCompletionAction。这是一个内部接口,用于向任务添加完成操作 - 延续任务的轻量级版本(因为它是一个简单的操作)。此接口定义单个方法Invoke(Task),应在任务完成执行时调用该方法。 Task有一个内部方法,允许添加这些轻量级延续:

internal void AddCompletionAction(ITaskCompletionAction action)

现在回到WhenAllPromise<T>课程。它有两个字段:

private readonly Task<T>[] m_tasks;
private int m_count;

在初始化期间,此类将所有给定任务存储在字段数组中,初始化计数器,并为已完成的任务调用continuation或将其自身添加到任务完成操作:

m_tasks = tasks;
m_count = tasks.Length;

foreach (var task in tasks)
{
    if (task.IsCompleted) this.Invoke(task); // short-circuit
    else task.AddCompletionAction(this); // simple completion action
}

那就是它。任务不是由WhenAllPromise课程开始的。它仅使用在任务完成时调用的回调操作。在回调操作中,每次完成某些任务时,它会减少m_count,直到完成所有任务并且我们可以获取结果。