Task.WaitAll в списке в F # - PullRequest
       31

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

4 голосов
/ 25 февраля 2011

Я занимаюсь параллельным программированием с использованием 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 ]

5 голосов
/ 25 февраля 2011

Как объясняет Роберт, если вы хотите вызвать 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 для сбора всех результатов, поведение будет почти таким же.

3 голосов
/ 25 февраля 2011

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

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

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

1 голос
/ 25 февраля 2011

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

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

...