2016-04-14 2 views
2

Предположим, у меня есть коллекция Task s, которую я собираюсь на WaitAll(). Предположим, что до того, как все они закончили, я хочу добавить еще несколько задач в эту коллекцию, и я хочу, чтобы ожидание продолжалось до тех пор, пока не будет все сделано тоже. И я мог бы добавить еще больше задач до конца и т. Д. И т. Д.Добавить дополнительные задачи в существующий WaitAll

Могу ли я сделать это с помощью TPL? Или мне придется ручным способом управлять потоками? (Тьфу!)

WaitAll() действует на Task[], так что я не могу просто иметь List<Task>, что я называю .ToArray() дальше, потому что тогда Wait не будет знать о новых задачах, которые добавляются?

Аналогичные вопросы о WaitAny() применяются.

ответ

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)?

+0

Re Wait/When, потому что это обновление Threading в сердце огромного старого кода (замена «семафора QueueUserWorkItem' +' ManualResetEvent ») и с асинхронным и/или обратным вызовом, распространяющимся по всему остальная часть базы кода будет несостоятельной. – Brondahl

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 scenarithat придумать, если вы добавляете задачи в течение длительного времени, и у вас есть некоторые короткоживущие задачи изначально. например если мой внутренний список задач завершится через 5 секунд, я не могу добавить новую задачу в список ожидания через 10 секунд, потому что я уже буду ждать.

Смежные вопросы