I have a Task factory thats kicking off many tasks, sometimes over 1000. I add every Task to a list, and remove it when the Task has completed.
var scan = Task.Factory.StartNew(() =>
{
return operation.Run();
}, token.Token
);
operations.Add(scan);
When a task Completes:
var finishedTask = scan.ContinueWith(resultTask =>
OperationComplete(resultTask),
TaskContinuationOptions.OnlyOnRanToCompletion
);
public virtual void OperationComplete(Task task)
{
operations.Remove(task);
}
When all are complete:
Task.Factory.ContinueWhenAll(operations.ToArray(),
result =>
{
AllOperationsComplete();
}, TaskContinuationOptions.None);
Then, at certain points in my application I want to get the count of running tasks. (This is where I get the error: “Collection was modified; enumeration operation may not execute.”)
public int Count()
{
int running = operations.Count<Task>((x) => x.Status == TaskStatus.Running);
return running;
}
A couple questions:
1) Should I even worry about removing the tasks from the list? The list could easily be in the 1000s.
2) Whats the best way to make Count() safe? Creating a new List and adding operations to it will still enumerate the collection, if I remember right.
Either you need to lock to make sure only one thread accesses the list at a time (whether that’s during removal or counting) or you should use a concurrent collection. Don’t forget that
Count(Func<T, bool>)needs to iterate over the collection in order to perform the count – it’s like using aforeachloop… and you can’t modify a collection (in general) while you’re iterating over it.I suspect that
ConcurrentBagis an appropriate choice here – and as you’re using TPL, presumably you have the .NET 4 concurrent collections available…