Search code examples
f#async-workflow

Let! executing in sequence?


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.


Solution

  • 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