Asynchronous Programming Model (APM)

The Asynchronous Programming Model (APM) is one of the oldest patterns introduced by Microsoft in .NET 1.0 back in 2001 for asynchronous programming handling.

The pattern is easy. To start a deferred job, you simply start such a job by using a Delegate (remote method invoker) and then get an object back of type IAsyncResult to know the status of such a remote operation. Here an asynchronous programmed application to compute file hashes. The application will add a "." to the Starting data computation initial message to acknowledge to the user that the application is still processing. The following examples use the blocking approach:

static void Main(string[] args)
{
    //a container for data
    var complexData = new byte[1024];

    //a delegate object that gives us the ability to trigger the pointed method in async way
    var dataDelegate = new Action<byte[]>(ComputeComplexData);

    Console.Write("Starting data computation...");

    //start retrieving complex data in another thread
    IAsyncResult dataStatus = dataDelegate.BeginInvoke(complexData, null, null);

    //waiting the completation
    while (!dataStatus.IsCompleted)
    {
        Console.Write(".");
        Thread.Sleep(100);
    }

    Console.WriteLine(" OK");

    //instantiate a delegate for hash elaboration in async way
    var hashDelegate = new Func<byte[], string>(ComputeHash64);

    Console.Write("Starting hash computation...");

    IAsyncResult hashStatus = hashDelegate.BeginInvoke(complexData, null, null);

    //waiting the completion
    while (!hashStatus.IsCompleted)
    {
        Console.Write(".");
        Thread.Sleep(100);
    }

    //this time the async operation returns a value
    //we need to use the delegate again to catch this value from the other thread
    var hash = hashDelegate.EndInvoke(hashStatus);

    Console.WriteLine(" OK");

    Console.WriteLine("END");
    Console.ReadLine();
}

static void ComputeComplexData(byte[] data)
{
    var r = new Random();
    Thread.Sleep(3000);
    r.NextBytes(data);
}

public static string ComputeHash64(byte[] data)
{
    using (var engine = new System.Security.Cryptography.MD5CryptoServiceProvider())
    {
        Thread.Sleep(3000);
        var hash = engine.ComputeHash(data);
        return Convert.ToBase64String(hash);
    }
}

The code is very easy. The class that helps make things asynchronous here is the Delegate class. Here, we use the pre-generated versions, Action<T> and Func<T>, compliant with the generic pattern, which helps us use any feature required of such Delegate objects without having to declare a specific one each time.

Note

A Delegate object is an object-oriented method pointer with a lot of added features such as asynchronous support and multiple method handlers. Any CLR event is a Delegate too. Such a class gives us the ability to invoke any remote method in a synchronous way (with the usual .Invoke method), or in an asynchronous way with BeginInvoke/EndInvoke, as visible in the preceding example. As mentioned earlier, the IsCompleted property gives us feedback about the remote execution completion of all pointed remote methods.

The usage of such a blocking asynchronous operation, without the need to block the execution of the main thread, helps create respondent UXs as a download popup, or special import/export features.

Asynchronous Programming Model (APM)

A simple asynchronous download popup

There are a lot of SDKs that give us the ability to use APM patterns like in the preceding example. In .NET 4.0, many core-framework APM implementations have been updated to the new TAP framework (discussed later in this chapter in the Task-based Asynchronous Pattern (TAP) section). Here's another APM example showing network communication with the IDisposable/using pattern:

static void Main(string[] args)
{
    //running in .NET 4.0

    var url = "http://www.google.com";

    Console.Write("Asking for {0}", url);

    //create a new web request for given url
    var request = WebRequest.Create(url);

    //start collecting response in async way
    var responseStatus = request.BeginGetResponse(null, null);

    //waiting the completation
    while (!responseStatus.IsCompleted)
    {
        Console.Write(".");
        Thread.Sleep(100);
    }
    Console.WriteLine(" OK");

    //a size counter
    int size = 0;

    //catch back on the main thread the response
    using (var response = request.EndGetResponse(responseStatus))
    //open the stream to access the response data
    using (var stream = response.GetResponseStream())
    //open a reader for such data
    using (var r = new StreamReader(stream, Encoding.UTF8))
        //until data is valid
        while (!r.EndOfStream && r.Read() >= 0)
            size++;

    Console.WriteLine("Total size: {0:N1}KB", size / 1024D);

    Console.WriteLine("END");
    Console.ReadLine();
}

In the preceding second code example, we found the ability to use the APM with .NET assemblies. The usage is very straightforward. As visible in the two examples given, the IAsyncResult type gives us the ability to wait for completion in a polling way by repetitively checking the IsCompleted property value. The additional ability to wait for such completion with a WaitHandle class is interesting, as already seen in the Multithreading synchronization section in Chapter 3, CLR Internals. Here's an example:

//alternative 1:
//waiting the completation
while (!status.IsCompleted)
    Thread.Sleep(100);

//alternative 2:
//waiting the completation with the signaling event lock
status.AsyncWaitHandle.WaitOne();

Although the WaitHandle class based alternative may seem to be more comfortable than the one that uses the polling property check, the difference is that the polling one also gives us the ability to update the UI while waiting. Instead, the WaitHandle class will simply stop the execution of the thread where such an object is being waited on. Bear in mind that multiple threads can wait together at the same time as the IAsyncResult completion status or wait handle, without any issues (until this brings some other resources to the race condition).

In addition to the blocking wait, as said at the beginning of the paragraph, we have the ability to catch the completion of an asynchronous method execution, to another asynchronous method by passing a Delegate object, which represents a callback method. If multiple callbacks are caught in the same handler, a state parameter can help in its handling. Using a callback method gives us a more event-based approach. Consider that the callback executes in the same thread as the asynchronous processing. Here is an example with a single callback method:

static void Main(string[] args)
{
    var invoker = new Func<int>(OnCreateInteger);

    //trigger 3 invocations sending the same invoker as the state to the ending handler
    invoker.BeginInvoke(OnHandleInteger, invoker);
    Thread.Sleep(100);
    invoker.BeginInvoke(OnHandleInteger, invoker);
    Thread.Sleep(100);
    invoker.BeginInvoke(OnHandleInteger, invoker);

    Console.WriteLine("MAIN THREAD ENDED");
    Console.ReadLine();
}

private static void OnHandleInteger(IAsyncResult ar)
{
    //the state contains the sent invoker    var invoker = (Func<int>)ar.AsyncState;

    Console.WriteLine("Async operation returned {0}", invoker.EndInvoke(ar));
}

private static int OnCreateInteger()
{
    Thread.Sleep(3000);
    //returns a random integer
    return DateTime.Now.Millisecond;
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.44.182