Существует ли общая задача 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
Я уверен, что это безопасная операция даже с предупреждением, но если вы действительно хотели обойти это, лучшим вариантом, чем создание собственной реализации, было бы просто преобразовать ваш параметр tasks
в желаемый тип:
Task.WaitAll(tasks.Cast<Task>().ToArray())
Это убивает синие squiggles для меня, позволяет мне сохранить мою переменную tasks
generic и не заставлять меня создавать много нового страшного кода, который в конечном счете лишний.
Ответ 2
Общий метод Task.WaitAll будет означать, что все Задачи должны были бы возвращать тот же тип, который был бы крайне ограниченным. Написание чего-то подобного можно было бы сделать вручную (см. Ответ Брекельманса в ответ), но это не позволит продолжить или отмену без большой работы.
Простое решение, если вы не используете массив для чего-либо еще,
.ToArray<Task>();
Ответ 3
Вы можете создать метод расширения для этого.
Я не знаю точную реализацию 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
Вы можете изменить это для поддержки общих задач.
Ответ 4
ЛУЧШЕ И ПРОСТО ОТВЕТ
На самом деле есть аналогичная общая перегрузка:
Task all = Task.WhenAll(tasks)
Это отличается тем, что возвращает Task
, который будет завершен после выполнения всех задач. так что вы можете использовать await
или Wait()
, что хотите.
Посмотрите на подпись:
Перегрузки
--------- NON GENERIC OVERLOADS --------------
WhenAll(IEnumerable<Task>)
Создает задачу, которая будет завершается, когда все объекты Task
в перечисляемой коллекции имеют завершено.
WhenAll(Task[])
Создает задачу, которая завершится, когда все Task
объекты в массиве завершены.
--------- GENERIC OVERLOADS --------------
WhenAll<TResult>(IEnumerable<Task<TResult>>)
Создает задачу, которая будет завершается, когда все объекты Task<TResult>
в перечислимом Коллекция завершена.
WhenAll<TResult>(Task<TResult>[])
Создает задачу, которая будет выполнена когда все объекты Task<TResult>
в массиве завершены.