Jul 1, 2012

Multi Threading - How to report Asynchronous Method Progress/Status?

1 comment

Asynchronous Method Progress Reporting

This example explains how to report progress information and intermediate results from an asynchronous method. It builds on a previous example, Create an Asynchronous Method.

We will do the following modifications in the code from the example mentioned above:
  • modify the MyTaskWorker to report progress every one second
  • implement the MyTaskProgres­sChanged event with event data of type MyTaskProgres­sChangedEventAr­gs.


MyTaskWorker

This method is same as defined in previous example Create Asynchronous 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);
  }
}

Now we will modify this as it notifies the client code about progress percentage and an already processed file. Notice the AsyncOperation added to method's para­meters.

Code:
private void MyTaskWorker(string[] files, AsyncOperation async)
{
  for (int i = 0; i < files.Length; i++)
  {
    // a time consuming operation with a file (copy, compression, encryption etc.)
    Thread.Sleep(1000);

    // compute progress
    int progressPercentage = 100 * (i + 1) / files.Length;

    // raise the progress changed event
    MyTaskProgressChangedEventArgs eArgs = new MyTaskProgressChangedEventArgs(
      progressPercentage, files[i], null);
    async.Post(delegate(object e)
      { OnMyTaskProgressChanged((MyTaskProgressChangedEventArgs)e); },
      eArgs);
  }
}

After processing a file (the Thread.Sleep(1000) statement), we compute the progress in percentage and store it to MyTaskProgres­sChangedEventAr­gs together with the current file name, which is an example of an intermediate result sent to the client code.

Then, we fired the ProgressChanged event using the AsyncOperation object.
We also have to add the AsyncOperation async parameter to the MyTaskWorkerDe­legate and update the BeginInvoke statement in the MyTaskAsync method.

Code:
private delegate void MyTaskWorkerDelegate(string[] files, AsyncOperation async);

public void MyTaskAsync(string[] files)
{
  // ...
  worker.BeginInvoke(files, async, completedCallback, async);
  // ...
}

You may get doubt that, is two asyncs in BeginInvoke parameters are correct. Yes, they are.
The first async is passed to MyTaskWorker as its second parameter. The second async is stored into  IAsyncResult instance that is passed to MyTaskCompleted­Callback. 


MyTaskProgres­sChanged event

This event is raised whenever the asynchronous method wants to report progress to the client code. 

Code:
public event EventHandler<MyTaskProgressChangedEventArgs> MyTaskProgressChanged;

protected virtual void OnMyTaskProgressChanged(MyTaskProgressChangedEventArgs e)
{
  if (MyTaskProgressChanged != null)
    MyTaskProgressChanged(this, e);
}
The MyTaskProgres­sChangedEventAr­gs used by the event handler define a data member for storing the name of the last processed file. If you don't need to report such results, use ProgressChange­dEventHandler as the event data type.

Here is the definition of MyTaskProgres­sChangedEventAr­gs,

Code:
public class MyTaskProgressChangedEventArgs : ProgressChangedEventArgs
{
  private string _currentFile;

  public string CurrentFile
  {
    get { return _currentFile; }
  }

  public MyTaskProgressChangedEventArgs(int progressPercentage, string currentFile,
    object userState)
    : base(progressPercentage, userState)
  {
    _currentFile = currentFile;
  }
}

Observe that MyTaskProgres­sChangedEventAr­gs inherits from ProgressChange­dEventArgs and that the CurrentFile data member is declared as read-only.


Handling the MyTaskProgres­sChanged event

Register and implement a handler of the MyTaskProgres­sChanged even­t.

Code:
myObj.MyTaskProgressChanged +=
  new EventHandler<MyTaskProgressChangedEventArgs>(myObj_MethodProgressChanged);

void myObj_MethodProgressChanged(object sender, MyTaskProgressChangedEventArgs e)
{
    Console.WriteLine("[MyTask] Progress: {0} %, Current file: {1}",
        e.ProgressPercentage, e.CurrentFile);
}

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

1 comment :