Task.WaitAll on a list in F#
Asked Answered
I

3

6

I am doing parallel programming using F#. With fixed number of elements, for example with 2 elements a1, a2 and a function f, I can do as follows:

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

I wonder how I could do the same with a list of elements:

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 spots that Task.WaitAll couldn't accept Task< T > list as its parameter. Task.WaitAll can have Task [] as its argument but it makes no sense because I need to get Result for next computation.

Ionone answered 25/2, 2011 at 11:31 Comment(0)
G
9

As Robert explains, if you want to call WaitAll, you'll have to cast the elements sequence to the base type Task and then convert it to an array. You can define your extension member for Task to make the tas simpler:

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

I'm using array comprehension and cast instead of Seq.cast because Seq.cast takes untyped IEnumerable - so F# infers better type for the extension method.

Another option is not to call WaitAll at all - if you don't do it, the Result properties will block until a task completes. This means that you'll block the thread anyway (there may be a bit larger number of blockings, but I'm not sure if it affects performance too much). If you use List.map to collect all results, the behavior would be almost the same.

Gumption answered 25/2, 2011 at 13:53 Comment(0)
F
6

This is an unfortunate design. Task.WaitAll is using the c# params keyword to allow you to specify several arguments and have them be an array in the method. It also uses C#'s implicit casting so that you can give it Task<T>'s. In F# you have to do this yourself by casting explicitly and converting to array:

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

Now you can get the results from ts.

Felonry answered 25/2, 2011 at 12:57 Comment(0)
B
1

Array also has map, so there's no reason you can't put the tasks in an array.

Or you could convert to an array just for the waitall...

Bombe answered 25/2, 2011 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.