pad*_*pad 4 f# functional-programming list parallel-extensions
我正在使用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
Run Code Online (Sandbox Code Playgroud)
我想知道如何用一系列元素做同样的事情:
let ts = List.map (fun a -> Task.Factory.StartNew(fun () -> f a))
Task.WaitAll(ts)
List.map (fun (t: Task<_>) -> t.Result) ts
Run Code Online (Sandbox Code Playgroud)
Visual Studio发现Task.WaitAll无法接受Task <T>列表作为其参数.Task.WaitAll可以将Task []作为其参数,但它没有意义,因为我需要获取Result以进行下一次计算.
这是一个不幸的设计。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)
Run Code Online (Sandbox Code Playgroud)
现在您可以从 获取结果ts。
正如Robert解释的那样,如果要调用WaitAll,则必须将元素序列转换为基类型Task,然后将其转换为数组.您可以定义扩展成员,Task以使tas更简单:
type System.Threading.Tasks.Task with
static member WaitAll(ts) =
Task.WaitAll [| for t in ts -> t :> Task |]
Run Code Online (Sandbox Code Playgroud)
我正在使用数组理解和强制转换而不是Seq.cast因为使用Seq.cast无类型IEnumerable- 因此F#为扩展方法推断出更好的类型.
另一种选择是根本不调用WaitAll- 如果不这样做,Result属性将阻塞,直到任务完成.这意味着你无论如何都会阻塞线程(可能会有更多的阻塞,但我不确定它是否会影响性能).如果您使用List.map收集所有结果,行为将几乎相同.