lunedì 6 gennaio 2014

Porting of AsyncOneManyLock to F#

If you have read the awesome book by Jeffrey Richter CLR via C# 4 ed. you have discovered that there are more optimal ways for thread synchronization than the one provided by the BCL. One of them is the use of new asynchronous capabilities in order to create an asynchronous synchronization primitive. In the book it is presented an AsyncOneManyLock which is used for thread synchornization for code with a high demand for responsiveness and scalability.
If you are an F# developer you know that the F# Asynchornous Workflow  and the Task Parallel Library are different, so I decided to port this useful piece of code to F# and show you how to use it with an example.

You can download the source code from here.

A simple example of usage of AsyncOnManyLock, we try to synchronize the access to a shared list from different kind of consumers as follows:

open System
open System.Collections.Generic
open System.Threading
open System.Threading.Tasks
open AP.Threading

[]
let main argv = 
    let asyncOneManyLock = new AsyncOneManyLock()
    let rnd = new Random()
    let col = new List()
    col.Add(1)
    
    async {
        while(true) do 
            let! discard = asyncOneManyLock.WaitAsync(OneManyMode.Shared) |> Async.AwaitTask
            let elem = col |> Seq.head
            printfn "col[0]=%d; col.Count=%d" elem col.Count
            asyncOneManyLock.Release()

    } |> Async.Start

    async {
        while(true) do            
            Thread.Sleep(rnd.Next(10000))
            let! discard = asyncOneManyLock.WaitAsync(OneManyMode.Exclusive) |> Async.AwaitTask
            let numOfElement = rnd.Next(col.Count - 1)
            if numOfElement > 0 then
                col.RemoveRange(0, numOfElement)
            asyncOneManyLock.Release()

    } |> Async.Start

    async {
        while(true) do  
            let! discard = asyncOneManyLock.WaitAsync(OneManyMode.Exclusive) |> Async.AwaitTask
            col.Insert(0, rnd.Next())
            asyncOneManyLock.Release()

    } |> Async.RunSynchronously

    0



The example creates three concurrent tasks that try to access a shared list to read/delete or update the contained items. If the access is of read type then a simple shared lock is sufficient, but in order to modify the list you must access the lock in an exclusive mode. In order to use the TPL with the Async builder you must pass the Task to the Async.AwaitTask function.

That's all, now you can benefit of a highly efficient, not blocking, threat synchronization component.