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:

- 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.OK, MessageBoxIcon.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.OK, MessageBoxIcon.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.


- //update the progress on UI
- progressBar1.Invoke((Action)delegate { ReportProgress(count); });
Note - The sample attached has all the running code for you.
I hope you enjoyed this post cause I enjoyed creating this demo a lot.
Download the sample code.


Adrian LPosted Jul 6, 2012, 9:13 AM
Thanks for the great and very useful article. Sorry for the newbie question, but what if function ProcessFilesInParallel would be in another class instead of the UI Form1.cs? Any idea about how .ContinueWidth in another object could notify UI about the update without accessing winforms objects directly (lblResult, for example)? Thanks.