更新以更清楚地解释事情
我有一个运行许多任务的应用程序。有些是最初创建的,其他可以在以后添加。我需要一个等待所有任务完成的编程结构。一旦所有任务完成,其他一些代码就应该运行,以清理并完成其他任务生成的数据的最终处理。
我想出办法来做到这一点,但不会称之为优雅。所以我想看看是否有更好的方法。
我所做的是在ConcurrentBag(线程安全集合)中保留任务列表。在该过程开始时,我创建并向ConcurrentBag添加一些任务。如果创建了一个新任务,并且还需要在最终步骤之前完成,那么该过程就会发生这种情况,我也将它添加到ConcurrentBag中。
Task.Wait接受一组Tasks作为其参数。我可以将ConcurrentBag转换为数组,但是在调用Task.Wait之后,该数组不会包含添加到Bag中的任何任务。
所以我在do while循环中有两步等待过程。在循环体中,我对Bag生成的数组做了一个简单的Task.Wait。完成后意味着完成所有原始任务。然后在while测试中,我对ConcurrentBag生成的新数组进行了1毫秒的快速测试。如果没有添加新任务,或者任何新任务也完成,它将返回true,因此not条件退出循环。
如果它返回false(因为添加了一个未完成的新任务),我们返回并执行一个非定时的Task.Wait。然后冲洗并重复,直到完成所有新旧任务。
// defined on the class, perhaps they should be properties
CancellationTokenSource Source = new CancellationTokenSource();
CancellationToken Token = Source.Token;
ConcurrentBag<Task> ToDoList = new ConcurrentBag<Task>();
public void RunAndWait() {
// start some tasks add them to the list
for (int i = 0; i < 12; i++)
{
Task task = new Task(() => SillyExample(Token), Token);
ToDoList.Add(task);
task.Start();
}
// now wait for those task, and any other tasks added to ToDoList to complete
try
{
do
{
Task.WaitAll(ToDoList.ToArray(), Token);
} while (! Task.WaitAll(ToDoList.ToArray(), 1, Token));
}
catch (OperationCanceledException e)
{
// any special handling of cancel we might want to do
}
// code that should only run after all tasks complete
}
有更优雅的方法吗?
答案 0 :(得分:1)
我建议您使用ConcurrentQueue
并在等待时删除项目。由于队列的先进先出性质,如果您到达队列中没有任何内容的地步,您就知道您已经等待已添加的所有任务到那时。
ConcurrentQueue<Task> ToDoQueue = new ConcurrentQueue<Task>();
...
while(ToDoQueue.Count > 0 && !Token.IsCancellationRequested)
{
Task task;
if(ToDoQueue.TryDequeue(out task))
{
task.Wait(Token);
}
}
答案 1 :(得分:0)
使用Microsoft的Reactive Framework(NuGet&#34; Rx-Main&#34;)这是一种非常酷的方式。
var taskSubject = new Subject<Task>();
var query = taskSubject.Select(t => Observable.FromAsync(() => t)).Merge();
var subscription =
query.Subscribe(
u => { /* Each Task Completed */ },
() => Console.WriteLine("All Tasks Completed."));
现在,要添加任务,只需执行以下操作:
taskSubject.OnNext(Task.Run(() => { }));
taskSubject.OnNext(Task.Run(() => { }));
taskSubject.OnNext(Task.Run(() => { }));
然后发出完成信号:
taskSubject.OnCompleted();
重要的是要注意信令完成不会立即完成查询,它将等待所有任务完成。信号完成只表示您将不再添加任何新任务。
最后,如果你想取消,那就这样做:
subscription.Dispose();