Sunday, April 17, 2011

Using Async for Multi-Threading (Alan Berman)

Asynchronous programming has been around for awhile in Visual Studio.  The new Async CTP feature, with the Await statement, provides a simpler, more organized code pattern than existing alternatives for asynchronous programming.  A big side-benefit that falls out is that the same code pattern can be used to implement some types of multi-threading.
Asynchronous processing allows the program to continue executing during a long wait, such as during data requests over the internet.  Multi-threaded processing helps to optimize code execution for a large number of very short waits, such as data requests from a local hard drive.  Multithreading also gives Windows the discretion to assign each thread to a different processor core. The differences are also discussed in the Async Feature Control Flow blog.
Doing multi-threading with the Await statement can simplify your code, and result in a code base that uses a single pattern for asynchronous and multi-threaded code.  Someone looking at your code later on won't need to refresh their memory on the nuances of thread synchronization.
Caveat:  If you have a large-scale application, or one that needs to be highly optimized, it may be better to continue to multi-thread using System.Threading types.  The Await statement has additional overhead to store and retrieve a state machine.  You can create metrics to compare two techniques.
This is an example of conventional processing with threads.  It uses an AutoResetEvent for thread synchronization.
Imports System.Threading
Module Module1
    Sub Main()
        ' Process using threads.
        ProcessWithThreads()
        Console.WriteLine("done")
        Console.ReadKey()
        ' Output:
        '  starting worker thread
        '  main thread processing
        '  worker thread processing
        '  sending signal in worker thread
        '  received signal in main thread
        '  done
    End Sub
    Private autoEvent As New AutoResetEvent(False)
    <MTAThread()> _
    Private Sub ProcessWithThreads()
        Console.WriteLine("starting worker thread")
        ThreadPool.QueueUserWorkItem(AddressOf ThreadWorker, autoEvent)
        Console.WriteLine("main thread processing")
        ' Wait for work method to signal.
        autoEvent.WaitOne()
        Console.WriteLine("received signal in main thread")
    End Sub
    Private Sub ThreadWorker(ByVal stateInfo As Object)
        Console.WriteLine("worker thread processing")
        ' Do some work.
        Thread.Sleep(1500)
        ' Signal that work is finished.
        Console.WriteLine("sending signal in worker thread")
        CType(stateInfo, AutoResetEvent).Set()
    End Sub
End Module
The following example is the Async equivalent.  It uses TaskEx.Run to start the worker thread.  The Await statement waits for the worker method to complete.  The fact that the AsyncWorker method is complete signals that the worker thread is complete.

Imports System.Threading.Tasks