Task.WaitAll в списке в F#

Я делаю параллельное программирование с использованием F#. С фиксированным количеством элементов, например, с 2 элементами a1, a2 и функцией f, я могу сделать следующее:

let t1 = Task.Factory.StartNew(fun () -> f a1)
let t2 = Task.Factory.StartNew(fun () -> f a2)
Task.WaitAll(t1, t2)
t1.Result, t2.Result

Интересно, как я мог сделать то же самое со списком элементов:

let ts = List.map (fun a -> Task.Factory.StartNew(fun () -> f a))
Task.WaitAll(ts)
List.map (fun (t: Task<_>) -> t.Result) ts

Visual Studio обнаруживает, что Task.WaitAll не может принять список Task в качестве параметра. Task.WaitAll может иметь Task [] в качестве аргумента, но это не имеет смысла, потому что мне нужно получить Result для следующих вычислений.

3 ответа

Решение

Как объясняет Роберт, если вы хотите позвонить WaitAllвам нужно будет привести последовательность элементов к базовому типу Task а затем преобразовать его в массив. Вы можете определить свой добавочный член для Task чтобы сделать вкус проще:

type System.Threading.Tasks.Task with
  static member WaitAll(ts) =
    Task.WaitAll [| for t in ts -> t :> Task |]

Я использую понимание массива и приведение вместо Seq.cast так как Seq.cast принимает нетипизированный IEnumerable - поэтому F# выводит лучший тип для метода расширения.

Другой вариант не звонить WaitAll вообще - если вы этого не сделаете, Result свойства будут блокироваться, пока задача не завершится. Это означает, что вы все равно заблокируете поток (может быть немного большее количество блокировок, но я не уверен, сильно ли это влияет на производительность). Если вы используете List.map чтобы собрать все результаты, поведение будет почти таким же.

Это неудачный дизайн. Task.WaitAll использует ключевое слово C# params, чтобы вы могли указать несколько аргументов и сделать их массивом в методе. Он также использует неявное приведение C#, так что вы можете дать ему Task<T>"S. В F# вы должны сделать это самостоятельно, приведя к явному преобразованию и преобразовав в массив:

let ts = [| 
     Task.Factory.StartNew(fun () -> 1)
     Task.Factory.StartNew(fun () -> 2)
     |]
Task.WaitAll(ts |> Seq.cast<Task> |> Array.ofSeq)

Теперь вы можете получить результаты от ts,

Массив также имеет карту, поэтому нет причин, по которым вы не можете поместить задачи в массив.

Или вы можете преобразовать в массив только для ожидания...

Другие вопросы по тегам