WaitAll for Changing List <任务>

时间:2015-07-17 22:02:22

标签: c# multithreading async-await task wait

更新以更清楚地解释事情

我有一个运行许多任务的应用程序。有些是最初创建的,其他可以在以后添加。我需要一个等待所有任务完成的编程结构。一旦所有任务完成,其他一些代码就应该运行,以清理并完成其他任务生成的数据的最终处理。

我想出办法来做到这一点,但不会称之为优雅。所以我想看看是否有更好的方法。

我所做的是在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
}

有更优雅的方法吗?

2 个答案:

答案 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();