Вопрос по generics, c# – Существует ли универсальный Task.WaitAll?

54

Я начинаю несколько параллельных задач, например:

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

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

<code>Task.WaitAll(tasks);
</code>

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

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

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

В этом случае преобразование безопасно, потому чтоWaitAll() не будет писать в массив. Есть ли причина, по которой вы хотите этого избежать? svick
есть ли шанс, что вы примете другой ответ? Лично я нашел других, более достойных, чем тот, который вы выбрали в качестве "принятого". Quibblesome
Также .Net 4.5 будет содержатьTask.WhenAll() который возвращает одинTask что завершается, когда всеTaskы в коллекции завершены. И у этого также есть универсальная версия, и это работает на любомIEnumerable<T> изTasks. svick
@svick спасибо за совет. Похоже, они переименовали то, о чем вы говорите, в «Когда все», так что вы можете просто сказать «ожидайте Task.WhenAll (task1, task2);» Simon_Weaver
@ Достаточно готово. Cristian Lupascu

Ваш Ответ

4   ответа
7

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

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

Затем позвоните, из вашего текущего кода:

tasks.WaitAll();

Edit

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

http://pastebin.com/u30PmrdS

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

Это действительно явно неправильно - вы выполняете свои задачиserially здесь, который не является реальнымWaitAll делает.
@MarkAmery это предполагает, что задачи уже отправляются, когда они передаются в этот метод.
Если люди настаивают на создании своих собственных (вместо использования одного из других решений, которые преобразуют универсальную коллекцию в массив Task), это лучший источник, с полными комментариями и ссылками на весь внутренний код, который вам также понадобится дубликат, чтобы он работал:referencesource.microsoft.com/#mscorlib/system/threading/Tasks/…
«Фактическая реализация» это довольно нетривиально. Не говоря уже о том, что вы теряете (или нуждаетесь в перекодировании), если в реализации .NET Framework есть исправления ошибок или улучшения производительности. Решения от @MerickOWA и DMac the Destroyer (в таком порядке) решают проблему OP очень просто.
25

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

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

  .ToArray<Task>();
+1, это возможно, но в моем случае я фактически использую массив, чтобы получитьint Результаты:var results = tasks.Select(task => task.Result) Cristian Lupascu
@ W0lf, затем просто сделайте дополнительный .ToArray & lt; Task & gt; (), прежде чем передать его в Task.WaitAll, любое возможное назначение этой скопированной Task [] не может вызвать исключение во время выполнения.
1
A DIFFENENT ANSWER

На самом деле тамIS похожая общая перегрузка:

Task all = Task.WhenAll(tasks)

Это отличается в этом он возвращаетTask это будет завершено после выполнения всех задач. так что вы можете использоватьawait на нем илиWait(), что вы хотите.

Посмотрите на подпись:

Overloads

--------- NON GENERIC OVERLOADS --------------

WhenAll(IEnumerable<Task>) Creates a task that will complete when all of the Task objects in an enumerable collection have completed.

WhenAll(Task[]) Creates a task that will complete when all of the Task objects in an array have completed.

--------- GENERIC OVERLOADS --------------

WhenAll<TResult>(IEnumerable<Task<TResult>>) Creates a task that will complete when all of the Task<TResult> objects in an enumerable collection have completed.

WhenAll<TResult>(Task<TResult>[]) Creates a task that will complete when all of the Task<TResult> objects in an array have completed.

26

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

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

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

Это отлично сработало для меня. Протестировал его как с задачами, которые выполнялись до конца, так и с задачами, которые истекли по времени, используяvar ifAllTasksRanToCompletion = Task.WaitAll(tasks.Cast<Task>().ToArray(), timeout: TimeSpan.FromSeconds(20));.

Похожие вопросы