假设我有一个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
没有此类事件。有没有什么好方法可以完成我正在寻找的东西?像这样:
答案 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.Count
和foreach (DataItem item in sources)
查看未处理的项目。
(与您的解决方案的不同之处在于您无法看到当前正在处理的项目)
答案 2 :(得分:1)
使用ContinueWith设置从集合中删除任务的操作。