我的 http 请求会随着任务的创建而启动吗
通过Task.WhenAll?或者它们不会在 await 进一步下降之前启动?
当您将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;
在初始化期间,该类将所有给定任务存储在字段数组中,初始化计数器,并为已完成的任务调用延续或将自身添加到任务完成操作中:
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,直到所有任务都完成并且我们可以获取结果。