Thursday, September 16, 2010

F# Async Computation Expressions: A Tiny Model System

Continuing my quest to learn computation expressions, I decided to explore from scratch the implementation of basic asynchronous behavior. This post is the result. It’s not too fancy, and lacks basic safety, resource management, etc., SO DON’T SIMPLY COPY AND USE IT IN REAL LIFE, but it gets the basic idea across. For real-world use, browse the Microsoft-supplied F# async classes (Microsoft.FSharp.Control.Async, etc.); you’ll find better stuff there than I could come up with. My goal here was to temporarily strip away most of the complexity, however necessary it is in real life, to investigate an underlying model.

My computation class below is just a simple thread spinner. It starts a thread process and then continues with the computation. As each bit completes, its caller waits for its own process to join, until the initial call joins and control continues at the module level. A test function in the form of a simple string printer is supplied. The screenshot below records a typical run.



As always: this code and other information is presented "as-is" and without warranty or implied fitness of any kind; use at your own risk. This is especially true in this case! Multi-threading can be a tricky business, and again I recommend you treat my code here as an oversimplified example, and use the Microsoft.FSharp.Control libraries for real-world use.

open System.Threading
 
 
/// A simple thread-runner.
/// Note: don't use this in real life!
/// Write something with better safety,
/// resource management, etc., or better yet,
/// use the F# supplied Async libraries!
type OverlySimpleAsync () = 
 
  /// Starts a thread and moves on,
  /// then waits for a join.
  member this.Bind (tp,f) =
    let t = Thread(ThreadStart(tp))
    t.Start()
    f()
    t.Join()
 
  /// Does nothing.
  member this.Return a = a
 
 
// Simple test.
 
/// This simple test prints a string
/// parameter a random number of times
/// with a random sleep between.
let r = System.Random()
 
let testThreadProc a =
  (fun () ->
    let count = r.Next(10,20)
    let sleep = r.Next(10,500)
    for i=0 to count do
      printf "%s" a
      Thread.Sleep(sleep))
 
// Spin some threads.
 
do OverlySimpleAsync() {
  do! testThreadProc "A"
  do! testThreadProc "B"
  do! testThreadProc "C"
  do! testThreadProc "D" 
}
 
printfn ""
printfn "Your breakpoint here."

No comments: