Reputation: 8597
Supposing I have a collection of Task
s, which I'm going to WaitAll()
on.
Suppose that, before they've all finished, I want to add some more tasks to that collection and I want the wait to continuing waiting until they're all done too. And I might add still more tasks before the end, etc. etc.
Can I do that using TPL? Or am I going to have to hand-roll my own thread management? (yuck!)
WaitAll()
acts on Task[]
so I can't just have a List<Task>
that I call .ToArray()
on, because then the Wait won't know about any new Tasks that are added?
Similar questions about WaitAny()
apply.
Upvotes: 2
Views: 561
Reputation: 401
If you want to synchronously wait on something and then wait on something else later, you need to cancel the original wait and start a new one. Do the original wait on what you have at the beginning, then when you need to add more tasks, add them to the current task list and signal the cancellation which restarts the wait.
public class MutableTaskWaiter
{
private List<Task> _tasks = new List<Task>();
private CancellationTokenSource _cts;
public IEnumerable<Task> Tasks
{
get
{
lock (_tasks)
{
return _tasks.ToArray();
}
}
}
public void WaitAll(IEnumerable<Task> tasks)
{
WaitMoreTasks(tasks);
do
{
try
{
_cts = new CancellationTokenSource();
Task.WaitAll(_tasks.ToArray(), _cts.Token);
}
catch (OperationCanceledException)
{
// start over and wait for new tasks
}
}
while (_cts.IsCancellationRequested);
}
public void WaitAny(IEnumerable<Task> tasks)
{
WaitMoreTasks(tasks);
do
{
try
{
_cts = new CancellationTokenSource();
Task.WaitAny(_tasks.ToArray(), _cts.Token);
}
catch (OperationCanceledException)
{
// start over and wait for new tasks
}
}
while (_cts.IsCancellationRequested);
}
public void WaitMoreTasks(IEnumerable<Task> tasks)
{
lock (_tasks)
{
_tasks.AddRange(tasks);
if (_cts != null)
{
// signal the wait to restart with the updated task list
_cts.Cancel();
}
}
}
}
Of course you are still going to have to deal with race conditions in the WaitAll scenarithat come up if you are adding Tasks for a long time and you have some short lived tasks originally. e.g. if my intial task list completes after 5 seconds, I cant add a new task to the wait list after 10 seconds because I'll already be done waiting.
Upvotes: 0
Reputation: 27871
Here is one solution for WaitAll
:
Assume that you have the following list to hold the tasks:
List<Task> tasks = new List<Task>();
Here is how you can wait on them:
while (true)
{
Task[] my_tasks;
lock (tasks)
{
my_tasks = tasks.ToArray(); //take snapshot
tasks.Clear(); //clear list
}
if (my_tasks.Length == 0)
break;
Task.WaitAll(my_tasks);
}
Just make sure that you lock on the list when you add tasks to the list like this:
lock (tasks)
{
tasks.Add(...
}
By the way, is there a reason why you are synchronously waiting for the tasks instead of asynchronously (you are using WaitAll
and not WhenAll
)?
Upvotes: 1