Подтвердить что ты не робот

Существует ли общая задача 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()?)

4b9b3361

Ответ 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> в массиве завершены.