我有一个任务工厂开始执行许多任务,有时超过1000.我将每个任务添加到列表中,并在任务完成后将其删除。
var scan = Task.Factory.StartNew(() =>
{
return operation.Run();
}, token.Token
);
operations.Add(scan);
任务完成时:
var finishedTask = scan.ContinueWith(resultTask =>
OperationComplete(resultTask),
TaskContinuationOptions.OnlyOnRanToCompletion
);
public virtual void OperationComplete(Task task)
{
operations.Remove(task);
}
一切都完成后:
Task.Factory.ContinueWhenAll(operations.ToArray(),
result =>
{
AllOperationsComplete();
}, TaskContinuationOptions.None);
然后,在我的应用程序的某些点,我想得到运行任务的计数。 (这是我收到错误的地方:“收集被修改;枚举操作可能无法执行。”)
public int Count()
{
int running = operations.Count<Task>((x) => x.Status == TaskStatus.Running);
return running;
}
几个问题:
1)我是否应该担心从列表中删除任务?该列表很容易在1000年代。
2)什么是让Count()安全的最佳方法?如果我没记错的话,创建一个新列表并向其添加operations
仍会枚举该集合。
答案 0 :(得分:4)
您需要锁定以确保一次只有一个线程访问列表(无论是在删除还是计数期间),或者您应该使用并发集合。不要忘记Count(Func<T, bool>)
需要迭代集合才能执行计数 - 这就像使用foreach
循环...而且你不能修改集合(一般情况下) '重复它。
我怀疑ConcurrentBag
在这里是一个合适的选择 - 当你使用TPL时,大概你可以使用.NET 4并发集合......
答案 1 :(得分:1)
您需要确保在迭代时不修改集合。大多数收藏品都不支持。 lock
可能就足够了。
但是,您可能希望重新审视设计。长时间锁定集合可能会导致您希望从异步任务中获得的性能提升。
答案 2 :(得分:0)
鉴于代码已经将状态作为计数调用的一部分进行检查,并假设在所有任务都在集合中之前你没有进行计数,只是不删除它们似乎是最简单的答案。如果您决定将List替换为其他内容,请确保实际测量性能差异,特别是如果Count调用完成的次数相对于集合的大小较低。 :)
答案 3 :(得分:0)
您可以使用ConcurrentDictionary 跟踪你的任务(Concurrentbags不允许你删除特定的项目)。
ConcurrentDictionary<Task, string> runningTasks = new ConcurrentDictionary<Task, string>();
Task task = Task.Factory.StartNew(() =>
{
// Do your stuff
}).ContinueWith(processedTask => {
var outString; // A string we don't care about
runningTasks.TryRemove(processedTask, out outString);
});
runningTasks.TryAdd(task, "Hello I'm a task");
// Add lots more tasks to runningTasks
while (runningTasks.Count > 0)
{
Console.WriteLine("I'm still waiting...");
Thread.Sleep(1000);
}
如果你想做一个合适的“WaitAll
”(需要LINQ):
try
{
Task[] keys = runningTasks.Keys.Select(x => x).ToArray();
Task.WaitAll(keys);
}
catch { } // WaitAll will always throw an exception.
希望它有所帮助。