Take your time

There was a little discussion of the F# async form at work, so I though I’d set up a simple example demonstrating it. This pattern is a really neat way to get blocks of a computation executed on the threadpool avoiding the need to premanently allocate a thread to the computation.
 
We can get something to execute on the thread pool using a timer callback, so this code causes the string "hello" to be printed after a ten second delay.
 
let func = new System.Threading.TimerCallback(fun _ -> printfn "hello");;
new System.Threading.Timer(func, null, 10000, -1);;

We can define a type of object that is responsible for firing an operation after a ten second delay. We keep the timers in a list to avoid a garbage collection disposing them and hence stopping them from firing. We use Async.Primitive to build up an async object; this gives us a continuation to call in the success case and another to call in the failure case. The TimedIncrement method simply increments the incoming value, but does so after waiting for ten seconds. By using a Timer instance, we effectively release the current thread and then a new thread is grabbed from the threadpool by the Timer when it fires.

 
type TimedAction() =
  let mutable actions = []
  member this.TimedIncrement(x) =
    Async.Primitive (fun (cont,econt) ->
      let callback = new System.Threading.TimerCallback(fun _ -> cont (x+1))
      let next = new System.Threading.Timer(callback , null, 10000, -1)
      actions <- next :: actions
      ())
 
We can use a function to get the current thread id
 
let getId () = System.Threading.Thread.CurrentThread.ManagedThreadId;;
 
We can then use this in a workflow.
 
let worker (delay) =
 async { let timer = new TimedAction()
         do System.Threading.Thread.Sleep(delay * 1000 : int)
         let! result1 = timer.TimedIncrement(1)
         do printfn "Running on thread %d" (getId())
         let! result2 = timer.TimedIncrement(result1)
         do printfn "Then on thread %d" (getId())
         return result2
       }
 
Running some simple examples we can see how the threadpool threads are being reused.
 
> Async.Run(worker(0));;
Running on thread 10
Then on thread 10
val it : int = 3
> Async.Run(Async.Parallel [ worker(0); worker(1); worker(2) ]);;
Running on thread 7
Running on thread 10
Running on thread 7
Then on thread 7
Then on thread 7
Then on thread 7
val it : int array = [|3; 3; 3|]
 
Advertisements
This entry was posted in Computers and Internet. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s