2011-02-25 65 views
4

我正在使用F#进行并行编程。随着固定数量的元素,例如具有2分元素A1,A2和一个函数f,我可以做如下:F#中的列表中的Task.WaitAll

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无法接受任务< T>列表作为其参数。 Task.WaitAll可以将Task []作为参数,但它没有任何意义,因为我需要为下一次计算获取Result。

回答

5

正如Robert解释的那样,如果您想要调用WaitAll,则必须将元素序列转换为基本类型Task,然后将其转换为数组。您可以定义扩展成员Task使TAS简单:

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来收集所有结果,则行为将几乎相同。

1

数组也有地图,所以没有理由不能将任务放入数组中。

或者你可以转换成数组只为为WaitAll ...

3

这是一个不幸的设计。 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得到的结果。