Asynchronous Tasks and Synchronization on UI TPL .Net 4.0


Using TPL for parallel programming is now a cup of cake. It made life easier for those who are new to multithreaded programming. A bunch of code lines can be encapsulated as an individual task using the Task class and you have the flexibility to run the task in either Sync or Async mode. In Sync mode the UI freezes for a moment until the task gets completed since all the other threads are blocked. So for a good user experience you don't want to let your application screen freezes for a moment so the Async task is a good choice in such case.

This post is to show how to notify the UI when the Asycn task gets completed. Absolutely you will want to get notified when you Asycn task has completed its work/execution.

Task.ContinueWith() is the function that will execute the next task after completion of the invoking task.

Syntax:

.ContinueWith((result) =>
                {
                    
//UI updation code to perform
                }, 
new CancellationTokenSource().Token,TaskContinuationOptions.None
                
//Right way to synchronize the UI with the completion of invoking task on ContinueWith
                
TaskScheduler.FromCurrentSynchronizationContext());

Let's take an example of a simple windows forms application that can process images to generate the thumbnails:

First let design the UI like this:

Asynchronous Tasks and Synchronization on UI TPL .Net 4.0

[I'm still using WinForms just to save my time in creating demos .]

In the Form1.cs code behind file I've create a function called ProcessFilesInParallel() that will get enabled when the user selects the ParalledMode checkbox from the UI.  The MaintainQuality Checkbox is toggling in two functions and the limit files dropdown can limit the file processing and will Cancel the processing immediately if the limit is reached. I'm not going to explain the other functional code you can download the sample and analyze the code, but here we'll focus on  updating the UI while a task is in progress and when the task gets completed.

The main task to perform is to show the progress bar progressing and show a analytical summary of the task when completed. Below is the complete code of PracessfilesInParallel() method:

private void ProcessFilesInParallel()
{
    ParallelOptions parOpts = new ParallelOptions();
    parOpts.CancellationToken = cancelToken.Token;
    parOpts.MaxDegreeOfParallelism = 
Environment.ProcessorCount;

   
string[] files = Directory.GetFiles(sourcePath"*.jpg");
   
long count = 0;
    totalfiles = files.Length;
    btnCancel.Visible = 
true;
   
//--- Record the start time---
   
DateTime startTime = DateTime.Now;

   
try
    {
        
Task t1 = Task.Factory.StartNew(() =>
        {
            
try
            {
                
Parallel.ForEach(files, parOpts, currentFile =>
                {
                    
//Check if cancellation requested
                   
if (cancelToken.Token.IsCancellationRequested)
                    {
                        cancelToken.Token.ThrowIfCancellationRequested();
                    }

                   
string filename =Path.GetFileName(currentFile);
                            
                   
//Threadsafe updation to shared counter
                    count = 
Interlocked.Increment(ref count);

                   
if (islimited && fileLimit <= count)
                    {
                        cancelToken.Cancel();
                       
// MessageBox.Show("Limit reached fileLimit = " + fileLimit + " Count=" + count);
                    }

                   
if (isQuality)
                        GenerateThumbnail(filename);
                   
else
                        GetThumb(filename);

                   
//update the progress on UI
                     progressBar1.Invoke((
Action)delegate { ReportProgress(count); });
                });
            }
            
catch (OperationCanceledException ex)
            {
                progressBar1.Invoke((
Action)delegate { ReportProgress(0); });
                
MessageBox.Show(ex.Message"",MessageBoxButtons.OKMessageBoxIcon.Warning);
            }
            
//ContinueWith is used to sync the UI when task completed.
        }, cancelToken.Token).ContinueWith((result) =>
        {
            
//Note the time consumed here
            
TimeSpan elapsed = DateTime.Now.Subtract(startTime);
            TimeElapsed = (
int)elapsed.TotalSeconds" s : " + elapsed.Milliseconds + " ms";
            
//finally update the UI with the summary result
            lblResult.Invoke((
Action)delegate { lblResult.Text ="File Processed: " + count + " out of " + fileLimit + " Time Elapsed: " + TimeElapsed; });
        },
new CancellationTokenSource().Token,TaskContinuationOptions.None,TaskScheduler.FromCurrentSynchronizationContext());
    }
    
catch (AggregateException ae)
    {
        
MessageBox.Show(ae.InnerException.Message"",MessageBoxButtons.OKMessageBoxIcon.Error);
    }
}

As you can see I've used the control.Invoke() method because if you don't use it'll throw an error about cross thread operation is invalid or something like that. so you have to invoke the object in the currently executing thread.

Another important point is to use the TaskSchecutler.FromCurrentSynchronizationContext() function as a parameter as this is the best fit to use if you are going to update the  UI in the ContinueWith() task delegate.

Asynchronous Tasks and Synchronization on UI TPL .Net 4.0

So when you'll run the code the output would be something like this:

Asynchronous Tasks and Synchronization on UI TPL .Net 4.0

The code to show progress is also written in the function ProcessinParallel() method:

//update the progress on UI
progressBar1.Invoke((
Action)delegate { ReportProgress(count); });

Note - The sample attached has all the running code for you.

Asynchronous Tasks and Synchronization on UI TPL .Net 4.0

I hope you enjoyed this post cause I enjoyed creating this demo a lot.

Download the sample code.
 

Up Next
    Ebook Download
    View all
    Learn
    View all