2017-01-16 27 views
2

难道不可以将任务添加到Task.WhenAll已在等待的列表中吗?如何将任务添加到Task.WhenAll正在等待的集合中?

我想等待所有任务完成,并且有可能在初始化后创建新的任务。但是这里Task.WhenAll不会等待稍后添加到集合中的新任务。

List<Task> tasks = new List<Task>(); 

var task1 = Task.Run(async() => 
    { 
     Debug.WriteLine("task #1 started"); 
     await Task.Delay(TimeSpan.FromSeconds(20)); 
     Debug.WriteLine("task #1 finished"); 
    }); 

var task2 = Task.Run(async() => 
{ 
    Debug.WriteLine("task #2 started"); 
    await Task.Delay(TimeSpan.FromSeconds(30)); 
    Debug.WriteLine("task #2 finished"); 
}); 

var task3 = Task.Run(async() => 
{ 
    Debug.WriteLine("task #3 started"); 
    await Task.Delay(TimeSpan.FromSeconds(10)); 
    var inner = Task.Run(async() => 
    { 
     Debug.WriteLine("inner task started"); 
     await Task.Delay(TimeSpan.FromSeconds(40)); 
     Debug.WriteLine("inner task finished"); 
    }); 

    tasks.Add(inner); 

    Debug.WriteLine("task #3 finished"); 
}); 

tasks.Add(task1); 
tasks.Add(task2); 
tasks.Add(task3); 

await Task.WhenAll(tasks); 
Debug.WriteLine("All finished"); 

输出:

task #2 started 
task #3 started 
task #1 started 
task #3 finished 
inner task started 
task #1 finished 
task #2 finished 
All finished 
inner task finished < didn't wait for this to finish 
+0

如果你看看[源代码](https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,69351c6da968e5d1),你会看到集合/数组之前被复制过内部机制踢了进来,因此对原始集合的任何更改都不会被观察到。 –

+0

我认为你能做的最好的是手动循环--' while(tasks.Count> 0){等待Task.WhenAll(任务); tasks = tasks.Where(t =>!t.IsCompleted); }'作为草图 –

+0

@Damien_The_Unbeliever非常感谢。我认为这是最好的解决方案。你不会把它写成答案吗? – Blendester

回答

1

Task.WhenAll总是copies它实际上开始等待的前向任务引用。这意味着可以在等待过程中更新原始集合,但WhenAll完全忽略了这些更改。

我建议,而是在一个循环中运行WhenAll。沿着线的东西:

while(tasks.Count > 0) { 
    await Task.WhenAll(tasks); 
    tasks = tasks.Where(t => !t.IsCompleted); 
} 

(随着你是否希望具体细节离开tasks本身未修改,确切的正确的数据类型是什么变量,等等,留给由读者填写演习)

5

这是因为tasks.Add(inner)await Task.WhenAll(tasks)后执行。要回答您的问题,首先您需要清除任务#3任务内部之间的关联。 IMO的内在任务应该是任务#3的一部分。也就是说,任务#3无法完成,直到内部任务完成。

await inner; //instead of tasks.Add(inner); 
+0

谢谢。但那是为了示范。新任务可以从任何地方添加到“任务”。我的问题是,当方法到达结束时,我想要完成所有任务。也许我正在解决一个错误的问题 – Blendester