Jul 1, 2012

(Multi Threading) How to invoke an asynchronous method

1 comment
This example shows how to create/Invoke an asynchronous method conforming to the Event-based Asynchronous pattern

In this example, we will create an asynchronous method that processes a list of files.

Code:
private void MyTask(string[] files)
{
  foreach (string file in files)
  {
    // a time consuming operation with a file (copy, compression, encryption etc.)
    Thread.Sleep(1000);
  }
}
 

The asynchronous method implementation consists of the following members:
  • MyTaskWorker method – does the actual work
  • IsBusy property – indicates that an asynchronous operation is running
  • MyTaskAsync method – invokes the asynchronous operation
  • MyTaskCompleted­Callback method – called when MyTaskWorker completes
  • MyTaskCompleted event – notifies of the asynchronous operation completion.
Notice that, how members names are derived from the original method name(MyTask). This is the recommended way.

Now we will see the how to achieve the above mentioned,


MyTaskWorker method

This method will execute on background and does the actual work of the original synchronous method. In this example, it doesn't differ from the original method. Notice that we also provide a delegate definition for this method.

Code:
private void MyTaskWorker(string[] files)
{
  foreach (string file in files)
  {
    // a time consuming operation with a file (copy, compression, encryption etc.)
    Thread.Sleep(1000);
  }
}

private delegate void MyTaskWorkerDelegate(string[] files);

IsBusy property

This property indicates a running asynchronous operation. Implementation of this property is needed, when concurrent invocations of asynchronous operations are not supported.

Code:
private bool _myTaskIsRunning = false;
public bool IsBusy
{
  get { return _myTaskIsRunning; }
}

MyTaskAsync method

This method invokes the asynchronous operation and immediately returns. If an asynchronous operation is already running, an InvalidOperati­onException is thrown.

Code:
private readonly object _sync = new object();

public void MyTaskAsync(string[] files)
{
  MyTaskWorkerDelegate worker = new MyTaskWorkerDelegate(MyTaskWorker);
  AsyncCallback completedCallback = new AsyncCallback(MyTaskCompletedCallback);

  lock (_sync)
  {
    if (_myTaskIsRunning)
      throw new InvalidOperationException("The control is currently busy.");

    AsyncOperation async = AsyncOperationManager.CreateOperation(null);
    worker.BeginInvoke(files, completedCallback, async);
    _myTaskIsRunning = true;
  }
}

First, an AsyncOperation is created. This object is used by the worker thread to invoke client's event handlers on the proper thread or context for the given application model.
Next, the asynchronous operation is started in a separate thread by the BeginInvoke method. Besides the files parameter, the BeginInvoke method has two additional parameters:
  • A delegate of type AsyncCallback to a callback method that is called when the asynchronous operation completes.
  • A custom object that is stored in the AsyncState property of an IAsyncResult instance returned by the BeginInvoke method. The same instance of IAsyncResult is also passed to the callback method.
Make sure to throw no exceptions after the BeginInvoke statement.


MyTaskCompleted­Callback method

This method calls worker delegate's En­dInvoke method to finish the asynchronous operation and raises the MyTaskCompleted e­vent.

Code:
private void MyTaskCompletedCallback(IAsyncResult ar)
{
  // get the original worker delegate and the AsyncOperation instance
  MyTaskWorkerDelegate worker =
    (MyTaskWorkerDelegate)((AsyncResult)ar).AsyncDelegate;
  AsyncOperation async = (AsyncOperation)ar.AsyncState;

  // finish the asynchronous operation
  worker.EndInvoke(ar);

  // clear the running task flag
  lock (_sync)
  {
    _myTaskIsRunning = false;
  }

  // raise the completed event
  AsyncCompletedEventArgs completedArgs = new AsyncCompletedEventArgs(null,
    false, null);
  async.PostOperationCompleted(
    delegate(object e) { OnMyTaskCompleted((AsyncCompletedEventArgs)e); },
    completedArgs);
}
 
In the above code operations are performed in following sequence,
1. the original worker delegate instance and the AsyncOperation object are obtained
2. EndInvoke is called to free resources held by the asynchronous operation.
3. MyTaskCompleted event is fired through the AsyncOperation object.

MyTaskCompleted event

This event is raised when the asynchronous operation is completed.

Code:
public event AsyncCompletedEventHandler MyTaskCompleted;

protected virtual void OnMyTaskCompleted(AsyncCompletedEventArgs e)
{
  if (MyTaskCompleted != null)
    MyTaskCompleted(this, e);
}

That's it. Now we are done with creation of Asynchronous method.

If you feel this is helpful or you like it, Please share this using share buttons available on page.

1 comment :