【问题标题】:Task.WaitAll on a list in F#F# 列表中的 Task.WaitAll
【发布时间】:2011-02-25 11:31:48
【问题描述】:

我正在使用 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 以进行下一次计算。

【问题讨论】:

    标签: list f# functional-programming parallel-extensions


    【解决方案1】:

    正如 Robert 所解释的,如果您想调用 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 收集所有结果,则行为几乎相同。

    【讨论】:

      【解决方案2】:

      这是一个不幸的设计。 Task.WaitAll 使用 c# params 关键字来允许您指定多个参数并将它们作为方法中的数组。它还使用 C# 的隐式转换,以便您可以给它Task&lt;T&gt;。 在 F# 中,您必须自己通过显式转换并转换为数组来执行此操作:

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

      现在您可以从ts 获取结果。

      【讨论】:

        【解决方案3】:

        Array 也有 map,所以没有理由不能将任务放在数组中。

        或者您可以转换为仅用于等待的数组...

        【讨论】:

          猜你喜欢
          • 2016-05-15
          • 1970-01-01
          • 1970-01-01
          • 2011-11-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多