views:

112

answers:

2

I was under the impression that let! in f# was smart enough to excute sequences of assignments in parallell. However, the following sample displays a different behavior, assignment of a,b,c seems to execute synchronously.

    let sleep sec =
        async
            {
                System.Threading.Thread.Sleep(sec * 1000)
                return sec
            }

    let bar = async 
                {
                    let! a = sleep 1
                    let! b = sleep 3
                    let! c = sleep 3
                    return a+b+c
                }

    let foo = Async.RunSynchronously(bar)
    printfn "%d" foo

Is that how it is/should be?

And if I want to execute a,b,c in parallell, am I supposed to use Async.Parallell ... |> Async.RunSynchronously ... then?

The above sample is ofcourse useless , the real usecase would be something like query a DB and call some webservices at the same time.

+2  A: 

let!, in an async block (or more correctly "computation expression") executes the expression asynchronously but the block as a whole is still executed linearly. This is the benefit of async computation expressions: making a sequence of dependent asynchronous operations much easier to write by performing continuation passing for you.

(Other types of computation expression provide their own semantics for let!, yield!, etc.)

To perform parallel/concurrent execution you need multiple async expressions executed separately.

I was under the impression

You've misunderstood (quite understandably).

Richard
+6  A: 

As Richard points out, asynchronous workflows are still fully sequential. I don't think that any projects attempting to do fully automatic parallelization have been fully successful, because doing that is just too difficult.

However, asynchronous workflows still make parallelization easier. The key thing is that they make it possible to do waiting without blocking threads (which is essential for scalability) and they also support automatic cancellation and easy exception handling to make your life easier. There are various patterns that allow you to parallelize code in asynchronous workflows.

  • Task-based you can start your three tasks in background and then wait until all of them complete (this is probably what you were expecting, so here is how to write that explicitly):

    let bar = async  { 
      let! atask = sleep 1 |> Async.StartChild
      let! btask = sleep 3 |> Async.StartChild
      let! ctask = sleep 3 |> Async.StartChild
      let! a = atask
      let! b = btask
      let! c = ctask
      return a + b + c } 
    
  • Data-parallel - if you have multiple workflows of the same type then you can create a workflow that runs all of them in parallel using Async.Parallel. When you then use let! it runs all three tasks and waits until they complete:

    let bar = async  { 
      let! all = Async.Parallel [ sleep 1; sleep 3; sleep 3 ]
      return all.[0] + all.[1] + all.[2] } 
    

Don Syme has an article discussing various patterns based on asynchronous workflows and you can find a comprehensive example in financial dashboard sample

Tomas Petricek
excellent answer and interesting links
Francesco
`Async.StartAsChild` should be `Async.StartChild`, I believe.
wmeyer
@wmeyer: Yes, you're right - thanks for the correction, I fixed the answer.
Tomas Petricek