Welcome to MSDN Blogs Sign in | Join | Help

Introducing F# Asynchronous Workflows

[ Update: Robert pickering has a very nice summary of using asynchonous workflows with web services ]

F# 1.9.2.9 includes a pre-release of F# asynchronous workflows. In this blog post we'll take a look at asynchronous workflows and how you might use them in practice. Asynchronous workflows are an application of F#'s computation expression syntax.

Below is a simple example of two asynchronous workflows and how you can run these in parallel:

    let task1 = async { return 10+10 }

    let task2 = async { return 20+20 }

    Async.Run (Async.Parallel [ task1; task2 ])

Here:

  • The expression "async { return 10+10 }" generates an object of type Async<int>.  
  • These values are not actual results: they are specifications of tasks to run.
  • The expression "Async.Parallel [ task1; task2 ]" composes two taks and forms a new task.
  • This generates a new value of type Async<int[]>.
  • Async.Run takes this and runs it, returning the array [| 20; 40 |].
  • For the technically minded, the identifier async refers to a builder for a computation expression. You can also dig into the details of the F# implementation for more details on this.

You can try this example in F# Interactive from Visual Studio.

The above example is a bit misleading: asynchronous workflows are not primarily about parallelization of synchronous computations (they can be used for that, but you will probably want PLINQ and Futures). Instead they are for writing concurrent and reactive programs that perform asynchronous I/O where you don't want to block threads. Let's take a look at this in more detail.

Perhaps the most common asynchronous operation we're all do these days is to fetch web pages. Normally we use a browser for this, but we can also do it  programmatically. A synchronous HTTP GET is implemented in F# as follows:

    #light

 

    open System.IO

    open System.Net

   

    let SyncHttp(url:string) =

 

        // Create the web request object

        let req = WebRequest.Create(url)

 

        // Get the response, synchronously

        let rsp = req.GetResponse()

 

        // Grab the response stream and a reader. Clean up when we're done

        use stream = rsp.GetResponseStream()

        use reader = new StreamReader(stream)

 

        // Synchronous read-to-end, returning the result

        reader.ReadToEnd()

You can run this using:

    SyncHttp "http://maps.google.com"

 

    SyncHttp "http://maps.live.com"

But what if we want to read multiple web pages in parallel, i.e. asynchronously? Here is how we can do this using asynchronous workflows:

    let AsyncHttp(url:string) =

        async {  // Create the web request object

                 let req = WebRequest.Create(url)

                

                 // Get the response, asynchronously

                 let! rsp = req.GetResponseAsync()

                

                 // Grab the response stream and a reader. Clean up when we're done

                 use stream = rsp.GetResponseStream()

                 use reader = new System.IO.StreamReader(stream)

 

                 // synchronous read-to-end

                 return reader.ReadToEnd() }

[ Note: This sample requires some helper code, defined at the end of this blog post, partly because one fuction called BuildPrimitive didn't make it into the 1.9.2.9 release.  ]

Here AsyncHttp has type:

    val AsyncHttp : string -> Async<string>

This function accepts a URL and returns a Async task which, when run, will eventually generate a string for the HTML of the page we've requested. We can now get four web pages in parallel as follows:

    Async.Run

        (Async.Parallel [ AsyncHttp "http://www.live.com";

                          AsyncHttp "http://www.google.com";

                          AsyncHttp "http://maps.live.com";

                          AsyncHttp "http://maps.google.com"; ])

How does this work? Let's add some print statements to take a closer look:

    let AsyncHttp(url:string) =

        async {  do printfn "Created web request for %s" url

                 // Create the web request object

                 let req = WebRequest.Create(url)

                

                 do printfn "Getting response for %s" url

                 // Get the response, asynchronously

                 let! rsp = req.GetResponseAsync()

                

                 do printfn "Reading response for %s" url

                 // Grab the response stream and a reader. Clean up when we're done

                 use stream = rsp.GetResponseStream()

                 use reader = new System.IO.StreamReader(stream)

 

                 // synchronous read-to-end

                 return reader.ReadToEnd() }

When we run we now get the following output:

Created web request for http://www.live.com

Created web request for http://www.google.com

Getting response for http://www.live.com

Getting response for http://www.google.com

Created web request for http://maps.live.com

Created web request for http://maps.google.com

Getting response for http://maps.google.com

Getting response for http://maps.live.com

Reading response for http://maps.google.com

Reading response for http://www.google.com

Reading response for http://www.live.com

Reading response for http://maps.live.com

As can be seen from the above, there are multiple web requests in flight simultaneously, and indeed you may see the diagnostics output interleaved. Obviously, multiple threads of execution are being used to handle the requests. However, the key observation is that threads are not blocked during the execution of each asynchronous workflow. This means we can, in principle, have thousands of outstanding web requests: the limit being the number supproted by the machine, not the number of threads used to host them.

In the current underlying implementation, most of these web requests will be paused in the GetResponseAsync call. The magic of F# workflows is always in the "let!" operator. In this case this should be interpreted as "run the asynchronous computation on the right and wait for its result. If necessary suspend the rest of the workflow as a callback awaiting some system event."

The remainder of the asynchronous workflow is suspended as an I/O completion item in the .NET thread pool waiting on an event. Thus one advantage of asynchronous workflows is that they let you combine event based systems with portions of thread-based programming.

It is illuminating to augment the diagnostics with a thread id: this can be done by changing printfn to use the following:

    let tprintfn fmt =

        printf "[%d]" System.Threading.Thread.CurrentThread.ManagedThreadId

        printfn fmt

The output then becomes:

[9]Created web request for http://www.live.com

[9]Getting response for http://www.live.com

[4]Created web request for http://www.google.com

[4]Getting response for http://www.google.com

[9]Created web request for http://maps.live.com

[9]Getting response for http://maps.live.com

[9]Created web request for http://maps.google.com

[9]Getting response for http://maps.google.com

[12]Reading response for http://maps.google.com

[13]Reading response for http://www.google.com

[13]Reading response for http://www.live.com

[13]Reading response for http://maps.live.com

Note that the execution of the asynchronous workflow to fetch www.live.com "hopped" between different threads. This is characteristic of asynchronous workflows. As each step of the workflow completes the remainder of the workflow is executed as a callback.

The Microsoft.FSharp.Control.Async library type has a number of other interesting combinators and ways of specifying asynchronous computations. We'll be looking at some of these in future blog posts.Also, one solution to the asynchronous I/O puzzle is to use methods such as WebRequest.BeginGetResponse and WebRequest.EndGetResponse directly, or for streams use Stream.BeginRead and Stream.EndRead. You can see uses of these methods in the MSDN .NET sample of bulk asynchronous image processing that runs to about 190 lines. In a future blog post we'll look at how this program becomes a rather elegant 20 liner in F#, largely due to the use of async workflows.

Asynchronous workflows are essentially a way of writing simple continuation passing programs in a nice, linear syntax. Importantly standard control operators such as try/finally, use, while, if/then/else and for can be used inside these workflow specifications. Furthermore this style of writing agents matches well with functional programming: agents that are state machines can often be defined as recursive functions, and the actual information carried in each state passed as immutable data. Mutable data such as hash tables can also be used locally within a workflow as long as it is not transferred to other agents. Finally, message passing agents are particularly sweet in this style, and we'll lok at those in later blog posts.

One important topic in this kind of programming is exceptions. In reality, each asynchronous workflow runs with two continuations: one for success and one for failure. In later blog posts we'll take a look at how errors are handled and propagated by asynchronous workflows, or you can play around with the 1.9.2.9 implementation today.

In summary, we've seen above that asynchronous workflows are one promising syntactic device you can use to help tame the asynchronous and reactive parts of the asynchronous/reactive/concurrent/parallel programming landscape. They can be seen as a nice, fluid F#-specific surface syntax for common compositional patterns of accessing user-level task scheduling algorithms and libraries. They are also a primary use of the monadic techniques that underpin computation expressions and LINQ, and similar techniques have been used in Haskell (see Koen Classen's classic 1999 paper, and related work was reported by Peng Li and Steve Zdancewic at PLDI and by Chris Waterson at CUFP this year). 

I'll be talking more about asynchronous workflows at TechEd Europe 2007 in Barcelona, and they are also covered in Chapter 13 of Expert F#, which is entering the final stages of production as I write.

Some examples of the underlying techniques that might be used to execute portions of asynchronous workflows now or in the future are the .NET Thread Pool (used in F# 1.9.2.9), Futures and the CCR, all of which incorporate many advanced algorithms essential to good performance and reliability in these areas.  As we move ahead with the F# design in this space we will ensure that asynchronous workflows can be used effectively with all of these.

Enjoy!

----------------------

Finally, here is the extra code required for the web sample above. These functions will be included in future release of F#.

    module Async =

        let trylet f x = (try Choice2_1 (f x) with exn -> Choice2_2(exn))

 

        let protect econt f x cont =

            match trylet f x with

            | Choice2_1 v -> cont v

            | Choice2_2 exn -> econt exn

 

        let BuildPrimitive(beginFunc,endFunc) =

            Async.Primitive(fun (cont,econt) ->

                (beginFunc(System.AsyncCallback(fun iar -> protect econt endFunc iar cont),

                           (null:obj)) : System.IAsyncResult) |> ignore)

 

   

    type System.Net.WebRequest with

        member x.GetResponseAsync() =

            Async.BuildPrimitive(x.BeginGetResponse, x.EndGetResponse)

 

Published Thursday, October 11, 2007 12:28 AM by dsyme

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

Wednesday, October 10, 2007 9:16 PM by Techy News Blog » Introducing F# Asynchronous Workflows

# Techy News Blog &raquo; Introducing F# Asynchronous Workflows

Thursday, October 11, 2007 12:38 AM by Alex Rubinsteyn

# re: Introducing F# Asynchronous Workflows

I was initially suspicious of Microsoft co-opting ML but I have to admit that F# looks like a great language.

I'm curious, however, about the overlap I see in .NET concurrency features. Why are asynchronous workflows preferable to Tasks for IO? Similarly, what makes Tasks more suitable for parallel computation than asynchronous workflows?

Thursday, October 11, 2007 4:17 AM by Walter Stiers - Academic Relations Team (BeLux)

# Introducing F# Asynchronous Workflows

Don Syme's WebLog on F# and Other Research Projects has the announcement of F# 1.9.2.9 including a pre-release

Thursday, October 11, 2007 4:26 AM by Noticias externas

# Introducing F# Asynchronous Workflows

Don Syme&#39;s WebLog on F# and Other Research Projects has the announcement of F# 1.9.2.9 including

Thursday, October 11, 2007 4:47 PM by Justin

# re: Introducing F# Asynchronous Workflows

Man, that is really impressive. Cool stuff.

Friday, October 12, 2007 8:15 AM by Marco Russo

# Nuovi paradigmi verso il parallelismo

Non è la prima volta che tocco l&#39;argomento del parallelismo... il fatto è che rendere paralleli e

Saturday, October 13, 2007 7:03 AM by Jon Harrop

# re: Introducing F# Asynchronous Workflows

Awesome!

I'd love to see more examples about the new Asynchronous Workflows in F#: they look incredibly useful...

Monday, October 15, 2007 3:46 PM by Robert Pickering's Strange Blog

# Concurrency in F# - Understanding how Asynchronous Workflows Work

Concurrency in F# - Understanding how Asynchronous Workflows Work

Monday, October 15, 2007 4:08 PM by Don Syme's WebLog on F# and Other Research Projects

# Robert on "Understanding how Asynchronous Workflows Work"

Robert Pickering has just posted a nice blog entry showing how programs look if you don't have asynchronous

Wednesday, October 31, 2007 7:39 PM by Marco Russo

# F# diventa un prodotto

Poche settimane fa ho citato un post su una caratteristica di F# che è interessante anche se non si è

Friday, December 07, 2007 9:43 AM by Angel "Java" Lopez

# Recursos de F#

Quisiera comentar hoy algunos recursos en la web sobre el lenguaje F#, implementación de programación

Tuesday, December 18, 2007 8:00 PM by Don Syme's WebLog on F# and Other Research Projects

# Using Parallel Extensions from F#

A couple of weeks ago saw the release of the CTP of the Parallel Extensions to the .NET Framework ( download

Wednesday, December 19, 2007 6:57 PM by Parallel Programming with .NET

# F# and the Task Parallel Library

Over on his blog, Don Syme has a post about F# and Parallel Extensions : "Over the coming year I expect

Wednesday, December 19, 2007 6:58 PM by Noticias externas

# F# and the Task Parallel Library

Over on his blog, Don Syme has a post about F# and Parallel Extensions : &quot;Over the coming year I

Thursday, December 20, 2007 9:39 AM by Walter Stiers - Academic Relations Team (BeLux)

# Parallel Extensions from F#

Don Syme's WebLog on F# and Other Research Projects has this post: A couple of weeks ago saw the release

Thursday, December 20, 2007 12:04 PM by Paul Mooney

# Parallel Extensions Fun

Using Parallel Extensions from F# A couple of weeks ago saw the release of the CTP of the Parallel Extensions...

Sunday, January 13, 2008 12:20 AM by design UX Enabled

# Recursos de F#

F# es un lenguaje nacido en Microsoft Research, de la mano de Don Syme. Pero no es un lenguaje funcional

Tuesday, January 15, 2008 9:45 AM by Hell Is Other Languages

# Software Transactional Memory for F#

I have written a library for using software transactional memory in F#. The library exposes memory transactions...

Friday, January 25, 2008 7:31 AM by hour loan online payday

# advance cash loan term

Just 0 advance card cash credit payday loan uk

Sunday, January 27, 2008 11:23 AM by Don Syme's WebLog on F# and Other Research Projects

# Kean Walmsley on using F# Asynchronous Workflows to simplify concurrent programming in AutoCAD

On Friday Kean Walmsley posted an excellent article on Using F# Asynchronous Workflows to simplify concurrent

Sunday, January 27, 2008 12:08 PM by Noticias externas

# Kean Walmsley on using F# Asynchronous Workflows to simplify concurrent programming in AutoCAD

On Friday Kean Walmsley posted an excellent article on Using F# Asynchronous Workflows to simplify concurrent

Thursday, February 07, 2008 9:36 AM by nightwatch77

# re: Introducing F# Asynchronous Workflows

But what about true workflow engine built on top of 'async'? I mean, this could be really useful - concurrent handling of many business processes. But an important ingredient of business logic is the use of distributed transactions, or at least ' normal' database transactions. What would happen to such transaction in an async function when it switches between threads? Would transaction context be preserved?

Tuesday, March 18, 2008 11:38 PM by Contagious Curiosity

# I'm supposed to be writing, but...

Apologies in advance to Don Syme here. I'm weak, and easily distracted. F# is my muse. let WriteChaptersAsync

Tuesday, April 29, 2008 6:33 PM by Wei Lu

# re: Introducing F# Asynchronous Workflows

We have a C# library for the async BPEL workflow ( http://www.extreme.indiana.edu/multicore/SOX.htm ).

The library is built upon CCR for concurrency and relies on the C# yield return for the sequential illusion, and its workflow semantics are based on WS-BPEL standard. But the basic idea is quite close to this post

Monday, May 12, 2008 11:41 PM by Matthew Podwysocki

# Thinking in Concurrently in .NET

In recent posts, you&#39;ve found that I&#39;ve been harping on immutability and side effect free functions

Monday, May 12, 2008 11:43 PM by Matthew Podwysocki's Blog

# Thinking in Concurrently in .NET

In recent posts, you've found that I've been harping on immutability and side effect free functions.

Monday, February 23, 2009 11:18 AM by Change the world with Microsoft Technology

# What is F#?

These pages document F# as a research project. You can find out all about the latest happenings with

Sunday, March 01, 2009 5:46 AM by HomerS

# re: Introducing F# Asynchronous Workflows

What about ref-cells in async-workflows?

Can I use them in asynchronous functions just as I use standard values or do I have to lock them?

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
 
Page view tracker