2011-02-25 66 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得到的結果。