2

I've been trying to learn how to use asynchronous message based methods. Below is a simplified version of what I was trying to do. I'm trying to use a finite state machine within a MailboxProcessor within an object. Overall it appears that the logic can be much more straightforward compared to using event based methods. I have an issue though when I try to use Async.Parallel. In the code following the printfn "Eval %i" v statement is getting evaluated twice for i1 & i2 instead of just one time for each. Which leads me to believe that I am not properly using Async.Parallel. Is there an alternative method that should be used within an asynchronous workflow?

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
7
  • 1
    You don't need the async { } block. [ i1; i2 ] |> Seq.map (fun n -> n.Eval) |> Async.Parallel |> Async.RunSynchronously will work. Commented Jan 15, 2014 at 16:55
  • 1
    Thanks for the comment, but the actual implementation does not run synchronously. My question is on how to evaluate an array of async returns (the Eval method in this case) in parallel within an async workflow. See link for an larger example. Commented Jan 15, 2014 at 17:50
  • Maybe just pass the result of Async.Parallel into Async.Ignore |> Async.Start? What do you want to do with the results from Eval once they're done? Commented Jan 15, 2014 at 18:20
  • 1
    In fsi under VS2010, I only see the Eval 1 and Eval 2 printed out once with the code above, as would be expected. What's your environment? Commented Jan 15, 2014 at 18:40
  • 1
    I'm starting to wonder if I stumbled across a bug. It runs as expected if I change the nodeValues definition to let! nodeValues = [| i1; i2 |] |> Array.map(fun n -> n.Eval) |> Async.Parallel. Anyone run across different behavior between Seq.map versus Array.map within an async workflow before? Commented Jan 15, 2014 at 20:18

1 Answer 1

3

This is a bug in F# 3.0. Async.Parallel calls Seq.toArray twice. Run your code under F# 3.1 and it will only print once. Here's the fix in the F# source repository.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the help gradbot. I didn't think to check the github source. It looks like the Async.Parallel method has enough dependencies that it's more than just a minor snippet that I would need if I wanted to use the 3.1 methodology in VS2012. When I'm using Array.map and starting with an array going into the Async.Parallel method is the Seq.toArray call effectively getting ignored then? I guess what I'm asking does using Array.map in F# 3.0 solve the issue or do I need to avoid Async.Parallel in 3.0 altogether? Thanks!
Sure, and yes if you use Array.map it will work correctly. The bug will still happen in the F# source but it will just be converting your array into a sequence and then back into an array twice. I believe this is an design flaw. Async.parallel should take an array to begin with and not a sequence. It doesn't actually support lazy evaluation and this could lead some users astray.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.