2014-01-15 28 views
2

我一直在試圖學習如何使用基於異步消息的方法。以下是我正在嘗試做的簡化版本。我試圖在一個對象內的一個MailboxProcessor內使用一個有限狀態機。總的來說,與使用基於事件的方法相比,邏輯似乎更直接。當我嘗試使用Async.Parallel時,我遇到了一個問題。在printfn "Eval %i" v聲明之後的代碼中,對於每個i1 & i2而不是僅一次評估兩次。這導致我相信我沒有正確使用Async.Parallel。是否有異步工作流應該使用的另一種方法?如何在異步工作流中使用Async.Parallel?

type Input(v) = 

    let agent = 
     MailboxProcessor.Start(fun inbox -> 
      let rec loop() = 
       async { 
        let! (msg : AsyncReplyChannel<int>) = inbox.Receive() 
        printfn "Eval %i" v 
        msg.Reply(v) 
        return! loop() 
       } 
      loop()) 

    member this.Eval = agent.PostAndAsyncReply(fun r -> r) 

let i1 = Input(1) 
let i2 = Input(2) 

async { 
    let! nodeValues = [ i1; i2 ] 
         |> Seq.map(fun n -> n.Eval) 
         |> Async.Parallel 
    return nodeValues 
} 
|> Async.RunSynchronously 
+1

您不需要'async {}'塊。 '[i1; i2] |> Seq.map(fun n - > n.Eval)|> Async.Parallel |> Async.RunSynchronously'將起作用。 – Daniel

+1

感謝您的評論,但實際的實施不會同步運行。我的問題是如何在異步工作流中並行評估異步返回數組(在這種情況下是'Eval'方法)。有關更大的示例,請參見[link](https://gist.github.com/mndrake/8440854#file-utopia-fsx-L121)。 – mndrake

+0

也許只是將'Async.Parallel'的結果傳遞給'Async.Ignore |> Async.Start'?一旦他們完成,你想用'Eval'的結果做什麼? – Tarmil

回答

3

這是F#3.0中的一個錯誤。 Async.Parallel調用Seq.toArray兩次。在F#3.1下運行你的代碼,它只會打印一次。 Here's the fix in the F# source repository.

+0

感謝您的幫助gradbot。我沒想過要檢查github源代碼。它看起來像'Async.Parallel'方法有足夠的依賴關係,它不僅僅是我需要的一小段代碼,如果我想在VS2012中使用3.1方法。當我使用'Array.map',並開始一個數組進入'Async.Parallel'方法是'Seq.toArray'調用有效地被忽略呢?我想我問的是在F#3.0中使用'Array.map'解決了這個問題,還是我需要避免3.0中的Async.Parallel?謝謝! – mndrake

+2

當然,如果你使用Array.map,它會正常工作。該錯誤仍然會在F#源代碼中發生,但它只會將您的數組轉換爲一個序列,然後再次返回到數組中。我相信這是一個設計缺陷。 'Async.parallel'應該從一個數組開始,而不是一個序列。它實際上並不支持懶惰評估,這可能導致一些用戶誤入歧途。 – gradbot