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) => {
-
- }, new CancellationTokenSource().Token, TaskContinuationOptions.None,
-
- 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:
[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;
-
- DateTime startTime = DateTime.Now;
-
- try
- {
- Task t1 = Task.Factory.StartNew(() =>
- {
- try
- {
- Parallel.ForEach(files, parOpts, currentFile =>
- {
-
- if (cancelToken.Token.IsCancellationRequested)
- {
- cancelToken.Token.ThrowIfCancellationRequested();
- }
-
- string filename =Path.GetFileName(currentFile);
-
-
- count = Interlocked.Increment(ref count);
-
- if (islimited && fileLimit <= count)
- {
- cancelToken.Cancel();
-
- }
-
- if (isQuality)
- GenerateThumbnail(filename);
- else
- GetThumb(filename);
-
-
- progressBar1.Invoke((Action)delegate { ReportProgress(count); });
- });
- }
- catch (OperationCanceledException ex)
- {
- progressBar1.Invoke((Action)delegate { ReportProgress(0); });
- MessageBox.Show(ex.Message, "",MessageBoxButtons.OK, MessageBoxIcon.Warning);
- }
-
- }, cancelToken.Token).ContinueWith((result) =>
- {
-
- TimeSpan elapsed = DateTime.Now.Subtract(startTime);
- TimeElapsed = (int)elapsed.TotalSeconds + " s : " + elapsed.Milliseconds + " ms";
-
- 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.
So when you'll run the code the output would be something like this:
The code to show progress is also written in the function ProcessinParallel() method:
-
- 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.