2012-04-06 4 views
48

я начинаю несколько параллельных задач, например:Есть ли общий Task.WaitAll?

var tasks = 
    Enumerable.Range(1, 500) 
    .Select(i => Task.Factory.StartNew<int>(ProduceSomeMagicIntValue)) 
    .ToArray(); 

, а затем присоединиться к ним с

Task.WaitAll(tasks); 

На этой последнюю строку я получаю синий волнистый маркер под tasks, с предупреждающим сообщением:

Co-variant array conversion from Task[] to Task[] 
can cause run-time exception on write operation.

Я понимаю, почему я получаю это сообщение, но есть ли способ обойти это? (например, как общая версия Task.WaitAll()?)

+1

В этом случае преобразование безопасно, потому что 'WaitAll()' не будет записывать в массив. Есть ли причина, по которой вы хотите ее избежать? – svick

+11

Кроме того, .Net 4.5 будет содержать ['Task.WhenAll()'] (http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.whenall%28v=vs.110% 29.aspx), который возвращает одну «Задачу», которая завершается, когда все «Задачи» в коллекции завершены. И он также имеет общую версию, и он работает с любым 'IEnumerable ' 'Task'. – svick

+0

@svick thx для наконечника. похоже, что они переименовали то, о чем вы говорите, WhenAll, чтобы вы могли просто сказать «ждут задачи».WhenAll (task1, task2); –

ответ

8

Вы можете создать метод расширения, чтобы сделать это.

Я не знаю точное выполнение WaitAll, но мы можем предположить, что ждет каждый элемент для завершения:

static class TaskExtensions 
{ 
    public static void WaitAll<T>(this Task<T>[] tasks) 
    { 
     foreach (var item in tasks) 
     { 
      item.Wait(); 
     } 
    } 
} 

Затем вызовите из текущего кода:

tasks.WaitAll(); 

Редактировать

Реальная реализация немного сложнее. Я пропустил код из этого ответа, потому что он довольно длинный.

http://pastebin.com/u30PmrdS

Вы можете изменить это, чтобы поддержать общие задачи.

+0

«Фактическая реализация» довольно нетривиальна. Не говоря уже, вы теряете (или нуждаетесь в повторном кодировании), если в реализации .NET Framework есть исправления ошибок или улучшения производительности. Решения от @MerickOWA и DMac Destroyer (в этом порядке) очень просто разрешают проблему OP. – Mike

+0

Это, безусловно, неправильно - вы выполняете свои задачи * серийно * здесь, а это не то, что делает настоящий 'WaitAll'. –

+0

@MarkAmery предполагает, что задачи уже отправлены, когда они подаются в этот метод. – Bas

21

Общий метод Task.WaitAll будет означать, что все Задачи должны были возвращать тот же тип, который был бы крайне ограниченным. Написание чего-то подобного можно сделать вручную (см. Ответ Брекельманса в ответ), но это не позволит ContinueWith или отмену без большой работы.

Простое решение, если вы не используете массив для чего-нибудь еще есть

.ToArray<Task>(); 
+0

+1 Это возможно, но в моем случае я фактически использую массив, чтобы получить результаты 'int':' var results = tasks.Select (task => task.Result) ' – GolfWolf

+1

@ W0lf, а затем просто сделать лишний. ToArray (), прежде чем передать его в Task.WaitAll, любое возможное назначение этой скопированной задачи [] не может вызвать исключение во время выполнения. – MerickOWA

20

Я уверен, что это безопасная операция даже с предупреждением, но если вы действительно хотите обойти это лучше, чем создать свою собственную реализацию, просто переведите свой параметр tasks в нужный ему тип:

Task.WaitAll(tasks.Cast<Task>().ToArray()) 

Это убивает голубые загогулины для меня, позволяет мне держать мой tasks переменного родовым и не заставит меня создать много нового страшный код, который в конечном счете ненужный.

+1

Это отлично сработало для меня. Протестировал его как с задачами, которые выполнялись до завершения, так и с задачами, которые были рассчитаны по времени, с использованием команды var var. – Contango

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