如何在完成后从集合中删除任务

时间:2011-05-17 15:10:37

标签: c# .net-4.0 task-parallel-library

假设我有一个System.Threading.Tasks.Task

的集合
HashSet<Task> myTasks = new HashSet<Task>();

...我定期向集合中提供更多内容,因为我有更多需要处理的数据:

foreach (DataItem item in itemsToProcess)
    myTasks.Add(
        Task.Factory.StartNew(
            () => Process(item),
            cancellationToken,
            TaskCreationOptions.LongRunning,
            TaskScheduler.Default));    

由于Task在完成后仍保持TaskStatus.RanToCompletion状态而不是仅仅消失,因此它们将保留在集合中,直到明确删除并且集合将无限增长。需要修剪Task以防止这种情况发生。

我看过的一种方法是让Task访问集合并让它在最后删除。但我也在看一个架构,我必须删除我的组件尚未创建的任务。我的第一个想法是附加一个触发器或事件来完成每个任务,如下所示:

foreach (Task task in createdSomewhereElse)
{
    lock (myTasks) myTasks.Add(task);
    task.WhenTaskIsCompleted += 
        (o, ea) => { lock(myTasks) myTasks.Remove(task); };
    task.Start();
}

...但Task没有此类事件。有没有什么好方法可以完成我正在寻找的东西?像这样:

3 个答案:

答案 0 :(得分:14)

您当然可以在任务完成时附加触发器:Task.ContinueWith(及其通用等效项)。那对你来说可能已经足够了。

可能也希望将ConcurrentDictionary用作穷人的并发集 - 这样您在访问集合时就不必锁定。只需在迭代时使用Keys属性,并使用您喜欢的任何值作为值。

答案 1 :(得分:2)

为什么需要将任务保存在集合中?

为什么不使用基于BlockingCollection和Parallel.ForEach的解决方案

var sources = new BlockingCollection<DataItem>();

Task.Factory.StartNew(() => {
    Parallel.ForEach(sources.GetConsumingPartitioner(),
                     item => Process(item));
});

现在,您只需将商品送入封锁集合即可自动处理。

foreach (DataItem item in itemsToProcess)
    sources.Add(item);

您可以使用sources.Countforeach (DataItem item in sources)查看未处理的项目。 (与您的解决方案的不同之处在于您无法看到当前正在处理的项目)

答案 2 :(得分:1)

使用ContinueWith设置从集合中删除任务的操作。