2017-05-03 55 views
0

我是异步编程的新手。我想在等待任何任务之前启动一系列任务(发出http请求)。什么时候Task.WhenAll枚举?

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创建任务而被启动?或者等到进一步下去,他们才会开始?谢谢!

回答

1

通过Task.WhenAll创建任务 会启动我的http请求吗?或者等到进一步下去,他们才会开始?

当你传递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,直到完成所有任务并且我们可以获取结果。

1

WhenAll立即(并同步)将其可枚举参数。因此,所有任务将在WhenAll返回时开始。

如果您想到这一点,这是有道理的。 WhenAllmust知道它正在等待多少任务,以便知道自己的任务何时完成。此外,它必须链接到这些任务中的每一个,以便在每个子任务完成时通知它。没有其他时间做这项工作;它必须计数并在返回之前设置通知。