假设我有Task
的集合,我将WaitAll()
打开。{1}}。
假设在他们全部完成之前,我想在该集合中添加更多任务,我希望等待继续等待,直到他们全部完成。我可能会在结束之前添加更多任务等等。
我可以使用TPL吗?或者我将不得不手动滚动我自己的线程管理? (呸!)
WaitAll()
对Task[]
采取行动,因此我不能只打开List<Task>
我打电话给.ToArray()
,因为等待不会知道任何新任务那些被添加?
关于WaitAny()
适用的类似问题。
答案 0 :(得分:1)
以下是WaitAll
的一个解决方案:
假设您有以下列表来执行任务:
List<Task> tasks = new List<Task>();
以下是您可以等待它们的方法:
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);
}
只需确保在向列表添加任务时锁定列表,如下所示:
lock (tasks)
{
tasks.Add(...
}
顺便说一下,您是否有同步等待任务而不是异步的原因(您使用的是WaitAll
而不是WhenAll
)?
答案 1 :(得分:0)
如果您想要同步等待某些内容然后等待其他内容,则需要取消原始等待并开始新的等待。做原始等待你开始时的内容,然后当你需要添加更多任务时,将它们添加到当前任务列表并发出取消重新开始等待的信号。
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();
}
}
}
}
当然,如果你要长时间添加任务并且最初有一些短暂的任务,那么你仍然需要处理WaitAll中的竞争条件。例如如果我的初始任务列表在5秒后完成,我不能在10秒后将新任务添加到等待列表中,因为我已经等待了。