Motivation and Background
Lately I was getting agitated from the tedious callback pattern entrenched in the .NET Framework, most notably WinForms. Being far from pro, but still familiar with the the Android Java SDK, I found the async callback mechanism there enriching. It is simple, expressive yet powerful for the task of decoupling execution from the GUI thread. I naturally got inspired and having to do some WinForms job again, I firmly decided on coming with my own copycat in C#. From the official documentation we can see the description:
“AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers. AsyncTask
is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTask
s should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor
, ThreadPoolExecutor
, and FutureTask
. An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute
, doInBackground
, onProgressUpdate
, and onPostExecute
.”
From the same page we can see a brief example of asynchronous file download intended to be used directly from a GUI event handler. Instead I will show the same snippet but used in the form of anonymous class, which is more commonly found in use by Android developers.
new AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}.Execute("www.fileserver.com/filename.ext");
Pushing the Limits of C#
While trying to come up with an exact copy, following the semantics of the Java implementation I was pushing my C# skills to the maximum. At first, quite naively, I was hoping I could come with a nice abstraction in the form of IAsyncTask
generic interface. However, I quickly realized there isn’t really such thing as an anonymous classes in C#, but rather the notion of anonymous types in the form of object initializers. The subtle difference being the limitation that you can initialize only properties and fields and no way to override/redefine a function.
The following snippet demonstrates the issue:
class MyClass<T>
{
public T MyProp { get; set; },
public T GetMyProp() { return MyProp; }
}
var myvar = new MyClass<int>()
{
MyProp = 1,
int GetMyProp() { return -666 }
}
Another limitation in my opinion is the lack of support of void type as a valid generic type. Ideally I would like to do:
class MyClass<T1,T2> : MyBaseClass<T1,void,T3>
{}
Here I deleted a paragraph for the simple reason I did not want to turn the article into a rant, but rather I decided to share only one link, which I found most hilarious from all: (why void is not allowed in generics)
Anyway, as a result, I ended up with duplicating code. Not really a stopper, but something I would ultimately love to get rid of.
Final Realization
Haven’t I stumbled on the following article, by DaveyM69: GenericBackgroundWorker
. I might not even had the nerves left to write an entire article. However, having spent a weekend time all together of putting some working code and the WinForms demo project together, I felt simply obliged.
Since, C# let us dynamically initialize nothing else but field members and properties, the only sensible option I could think of was delegates exposed through properties.
My initial implementation is in the form of a simple AsyncTask
with three callback functions defined: Pre
, Do
, and Post
with two generic types Tin as parameter to Do
and Tout
as return value from Do
and input parameter to Post.
public class AsyncTask<Tin, Tout>{
private BackgroundWorker<Tin, int, Tout> bw;
public delegate bool PreFunc();
public delegate Tout DoFunc(Tin input);
public delegate void PostFunc(Tout output, Exception err);
public PreFunc Pre
{
get;
set;
}
public DoFunc Do
{
get;
set;
}
public PostFunc Post
{
get;
set;
}
public AsyncTask()
{
this.Pre = delegate { return true; };
this.bw = new BackgroundWorker<Tin, int, Tout>();
this.bw.DoWork += new EventHandler<DoWorkEventArgs<Tin, Tout>>(OnDoWork);
this.bw.RunWorkerCompleted +=
new EventHandler<RunWorkerCompletedEventArgs<Tout>>(OnRunWorkerCompleted);
}
public void Execute(Tin input)
{
if (Pre())
{
this.bw.RunWorkerAsync(input);
}
}
private void OnDoWork(object sender, DoWorkEventArgs<Tin, Tout> e)
{
Tout r = Do(e.Argument);
e.Result = r;
}
private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs<Tout> e)
{
Post(e.Result, e.Error);
}
}
Few things to notice about this sample code. Both Do
and Post
delegates needs to be defined, thus required, where Pre is entirely optional. I have achieved this by creating an anonymous function returning true in the constructor. The Pre function is meant as a precondition, returning only true in case we allow further execution.
The rest of the implementation is pretty straightforward, using the GenericBackgroundWorker
as you would normally use the native BackgroundWorker
.
For my later implementation I wanted to support progress reporting as well. That required a reference to AsyncTask
from within the anonymous Do
function, so that I can report the progress to the underlying BackgroundWorker
member. Not something a small indirection with tiny wrapper can’t solve.
Use and Purpose
The sole intended purpose of the AsyncTask
is to run any GUI long-term process in a background thread. Although, the use cases are not limited to GUI, but anywhere you require seamless decoupling using IoC between two threads. In my opinion a cleaner and less complicated solution in contrast to some famous patterns, like publish-subscribe pattern. By abstaining from using any C# style events we had cut on plenty of formalities and code size as result. But what I find most compelling about this solution is the clean intent of use that it demonstrate and at the same time somehow elegantly maintain separation of concerns for us.
In case you are not familiar with the C# lambda function expressions, you better check first the MSDN. There exist various ways with plenty of syntax sugar around readily available to you. At first the lambda syntax looked daunting to me. Disconnected from what I would consider classical C#, but you will end up loving the power it gives you above all.
new AsyncTask<int, int, string>(){
Pre = delegate
{
return true;
},
Go = progress =>
{
this.progressBar1.Value = progress;
},
Do = (input, progress) =>
{
progrss.Report(1);
Thread.Sleep(1000);
progrss.Report(100);
return "it took us 1 sec to finish";
},
Post = (output, err) =>
{
}
}.Execute(Tin);
You can use any type for the generic parameters, however I recommend using immutable value-types. The worst situation you might find yourself is sharing a reference between the background threads and running into well known plethora of threading issues. By rule of thumb you should keep the memory access confined to the local scope of the Do
function.
A final word of advice is to consider the fact that the same limitation stated in the Android documentation hold true in our current implementation too. That is, you should use AsyncTask
only for short-lived async operations. It has to do with the fact that the GenericBackgroundWorker
helper class utilizes the system thread-pool, in accordance with the original BackroundWorker
. Otherwise you risk starving the thread pool and as result defer execution of any further GUI actions in the process.
The WinForms Demo
The accompanying demo application constitutes a simple WinForms demo form with: a button to spawn the threads, a dropdown to specify number of threads and textbox to report misc. output . An overview of the form you can see in the beginning of this article.
Please make note of the define statement in the beginning of TestForm.cs, #define ADVANCED
. By simply commenting you can switch between the simple example of using AsynTask
<tin,tout> to the more advanced one AsyncTask
<tin,tgo,tout> with progress report.
#define ADVANCED
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace AsyncTaskCallback
{
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
this.progressBar1.Value = 0;
this.progressBar1.Maximum = 0;
}
private void OnGoClick(object sender, EventArgs e)
{
int count = int.Parse((string)(this.comboBox1.SelectedItem ?? "1"));
this.progressBar1.Maximum += count;
int i;
for (i = 1; i <= count; i++)
{
#if !ADVANCED
new AsyncTask<int, string>()
{
Do = input => {
int timeout = RandomRoulette();
Thread.Sleep(timeout);
return string.Format("T{0} returned after {1} secs",input,timeout/1000);
},
Post = (output, err) => {
PrintInTheGUI(output); }
}.Execute(i);
#endif
#if ADVANCED
new AsyncTask<int, int, string>()
{
Pre = delegate
{
return true;
},
Go = progress =>
{
this.progressBar1.Value += 1;
},
Do = (input, progress) =>
{
int timeout = RandomRoulette();
Thread.Sleep(timeout);
progress.Report(100, input);
return string.Format("T{0} returned after {1} secs", input, timeout / 1000);
},
Post = (output, err) =>
{
PrintInTheGUI(output);
}
}.Execute(i);
#endif
}
this.PrintInTheGUI("Threads spawned :" + (i - 1).ToString());
}
private static int RandomRoulette()
{
int timeout = new Random().Next(1, 13) * 1000;
return timeout;
}
private void PrintInTheGUI(string text)
{
this.textBox1.Text = text + System.Environment.NewLine + this.textBox1.Text;
}
}
}
Just for experimental purposes you might try using the “PrintOnGUIThread
” function inside the Do
function and see what the effects will be. You will get an IllegalOperationException
when trying to access an element of the GUI from outside the main GUI thread. The intended issue this article addresses.
Also, notice the visibility of the other function, which otherwise we can safely call from separate thread. Namely, the static function: RandomRoulette()
. Any static function is guaranteed to be thread safe on the CLR. The function itself returns the sleep timeout in milliseconds, at random anyway in the range of 1 to 13 seconds.
In the output textbox, first thing after pressing Start will be the print-out of how many threads we have requested. For a good reason I used the word requested and not spawned, since the threads get scheduled on the threadpool first and executed no more than X at time. Where X really depends on the number of async request you demand, leave it to the OS to figure it out. In case you are curious you can always pause execution in the debugger and have a look at the Threads window pane in Visual Studio 10. While there, you might as well enjoy the wonderful parallel stack provided. Well done VS
I could have easily disabled the Start Button once you invoke a single batch of async requests, however I made a small adaptation so that it is safe to request further batches, feel free to experiment a bit. Let me know if you encounter any runtime errors.
I guess that’s it. The self of the code is pretty straightforward to follow.
Realization based on TPL
Due to popular demand Here comes the same Contract based AsynTask
concept implemented using TPL. Task Parallel Library is a general purpose threading library living in the System.Threading.Tasks
namespace. It's goal is to simplify concurrent programming, while taking care of low level concerns as scheduling, state management etc. A good document (TAP.docx) can be find on MSDN giving a comprehensive overview of what it is all about together with usage examples. A more insights of the internal implementation you can find on the following blog post, in case you are really curious to peek under the hood.
I recommend first having some background, before jumping to new AsyncTask
implementation below.
public class AsyncTask<Tin, Tgo, Tout> where Tgo : struct
{
public class AsyncTaskCallBack : IProgress<Tgo>
{
private readonly AsyncTask<Tin, Tgo, Tout> asyncTask;
public AsyncTaskCallBack(AsyncTask<Tin, Tgo, Tout> at)
{
asyncTask = at;
}
public void ThrowIfCancel()
{
this.asyncTask.cancelToken.ThrowIfCancellationRequested();
}
public bool IsCancellationRequested
{
get
{
return this.asyncTask.cancelToken.IsCancellationRequested;
}
}
public void Report(Tgo value)
{
this.asyncTask.syncContext.Post(o => this.asyncTask.Go(value), null);
}
}
private readonly SynchronizationContext syncContext;
private readonly TaskCreationOptions taskCreateOptions;
private readonly CancellationToken cancelToken;
private readonly AsyncTaskCallBack callback;
private Task<Tout> task;
public Func<bool> Pre;
public Func<Tin, AsyncTaskCallBack, Tout> Do;
public Action<Tgo> Go;
public Action<Tout> Post;
public Func<Exception, bool> Err;
public AsyncTask(CancellationToken ct)
: this()
{
cancelToken = ct;
}
public AsyncTask(TaskCreationOptions tco = TaskCreationOptions.None)
{
this.Pre = delegate { return true; };
this.Err = delegate(Exception e) { return true; };
this.callback = new AsyncTaskCallBack(this);
this.taskCreateOptions = tco;
this.syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
}
public async void Execute(Tin input)
{
if (Pre())
{
Tout result = default(Tout);
try
{
result = await RunTaskAsync(input);
Post(result);
}
catch (OperationCanceledException oce)
{
if (Err(oce))
throw;
}
catch (AggregateException aex)
{
if (Err(aex.Flatten()))
throw;
}
catch (Exception ex)
{
if (Err(ex))
throw;
}
}
}
private Task<Tout> RunTaskAsync(Tin input)
{
this.task = Task.Factory.StartNew(() => Do(input, callback),
cancelToken,
taskCreateOptions,
TaskScheduler.Default);
return task;
}
private static void PerformInvoke(Control ctrl, Action action)
{
if (ctrl.InvokeRequired)
ctrl.Invoke(action);
else
action();
}
}
I will omit examining what it takes to come with async solution in C#, but rather focus on how it servers our purpose. As I have already mentioned, TPL is general purpose library, while the AsyncTask
proposed in this article is very specialized with one goal in mind, to decouple the GUI thread from the rest of your app. At the same time gives you a clear and safe Contract based implementation with no side effects.
First thing to notice is the very compact solution I came up with. I have introduced the predefined generic delegates, Action
and Func
, which come as part of the latest dotNet. The Execute
method is marked async
, which awaits
on the RunTaskAsync
method. Those two keywords async and awaits are the core of what constitutes a basic TPL solution. Another subtle difference is the optional CancelationToken
used for canceling a TPL task, more on canceling our AsyncTask
can be found in the next section. A good read on canceling and reporting can be found here.
Besides the cancellation token, we have yet another important player in the picture, SynchronizationContext
. One of the key responsibilities our AsyncTask
has is to successively delegate calls from any separate working thread to the main GUI thread, that's where the syncContext
fits in. I still keep the utility static method PerformInvoke(Control ctrl, Action action)
just for reference, what you need otherwise to call the GUI thread from different threading context.
Error handling and reporting is a topic of great interest to me. So I will try not to dwell on the topic in-depth, but simply point you to the quite traditional way of handling exceptions using a try-catch expression. Long story short, I found it the only working solution for me. However, there exists an alternative as far as async tasks are concerned, see Exception Handling (Task Parallel Library) , using continuations. However, I was loosing stack trace on my exceptions, which is a must have in my book. Something worth investigating. Read more on the topic in the conclusion.
In case a CancelationException
is thrown, which seems the recommended approach, I do handle it and propagate to the Err
function delegate. This might not be exactly what you're looking for.
The TPL DemoForm
public partial class TestForm : Form
{
private CancellationTokenSource cancelationTokenSource;
public TestForm()
{
InitializeComponent();
this.progressBar1.Value = 0;
this.progressBar1.Maximum = 0;
this.cancelationTokenSource = new CancellationTokenSource();
}
private void OnGoClick(object sender, EventArgs e)
{
int count = int.Parse((string)(this.comboBox1.SelectedItem ?? "1"));
this.progressBar1.Maximum += count;
this.cancelationTokenSource = new CancellationTokenSource();
int i;
for (i = 1; i <= count; i++)
{
new AsyncTask<int, int, string>(cancelationTokenSource.Token)
{
Go = progress =>
{
this.progressBar1.Value += 1;
},
Do = (input, progress) =>
{
int timeout = RandomRoulette();
if (timeout % 2000 == 0)
{
throw new InvalidOperationException("Muuuuuu...");
}
Thread.Sleep(timeout);
if (progress.IsCancellationRequested)
{
progress.ThrowIfCancel();
}
progress.Report(100);
return string.Format("T{0} returned after {1} secs", input, timeout / 1000);
},
Post = (output) =>
{
PrintInTheGUI(output);
},
Err = (exception) =>
{
this.progressBar1.Value += 1;
PrintInTheGUI("Holly C0W! An Exception: " + exception.Message);
return false;
}
}.Execute(i);
}
this.PrintInTheGUI("Threads spawned :" + (i - 1).ToString());
}
private static int RandomRoulette()
{
int timeout = new Random().Next(1, 13) * 1000;
return timeout;
}
private void PrintInTheGUI(string text)
{
this.textBox1.Text = text + System.Environment.NewLine + this.textBox1.Text;
}
private void OnCancel(object sender, EventArgs e)
{
this.cancelationTokenSource.CancelAfter(666);
PrintInTheGUI("Canceling......");
}
}
Having looked at the usage example above it looks quite similar to before. Still there are some points I consider worth mentioning.
Improved Contract Interface
To start, the interface is extended with a new Err function delegate. Before, following the original Android SDK design, the error was reported as parameter to the Post function delegate. Now, in case an exception got thrown from your Do
function it will get propagated to you Err
function, where you will have a chance to report to the user.
Pay Attention to the fact that the Err
returns a bool value whether to re-throw the exception or to consume it otherwise. Best practices dictates you should let the exception propagate up the stack.
TPL offers a suitable IProgress<T> interface
Still we need a way to yield back for cancellation in addition to progress reporting. So we still need that callback reference to the base AsyncTask
instance from within our new anonymous class. The class AsyncTaskCallBack
serves the purpose. It is passed as second parameter to our Do
function. It is named progress
, you might consider coming with more appropriate name to serve its dual purpose of reporting and canceling.
Random Exceptions
To demonstrate correct error handling some of the AsyncTask
s will throw from their Do
method. The random effect is dependent on the seconds to sleep time, whether it is even seconds. So every now and then you should get the Muu exception. You might consider switching VS debugger to not break automatically on exceptions.
The Cancelation token is external and shared
The cancellation token is provided as external dependency in the constructor of AsyncTask
. There are a few precautions as a result of it that you need to keep in mind. First, if you trigger the cancellation token, any future tasks you try to run will be in fault state on creation, hence never actually executed. Second, due to the aforementioned cause and affect I need to recreate new instance of the token object every time I schedule a new batch, otherwise after canceling once you will never be able to run yet another batch.
One last thing is: this.cancelationTokenSource.CancelAfter(666)
. 666 is nothing special, just to screw with religious people I use it quite often. The idea is to delay the actual call to the canceling mechanism only after the method yields back and we leave some time for the GUI to refresh. Consider, try it yourself, what happens when you invoke AsyncTask
1000 or more times and only after you try canceling.
Future Improvements
Old
One very intuitive way of improving is to cut our dependence on the GenericBackgroundWorker
. Although it is a very good implementation, it has plenty of unnecessary overhead. For one, it relies on the same events pattern found in C# I wanted to avoid at the first place. Support for canceling the async task. It is already supported by the GenericBackgroundWorker
so I will leave it to you to implement additional executor strategies, see for example (SERIAL_EXECUTOR
, THREAD_EXECUTOR
) of the original Android AsyncTask
. The current implementation is already giving you plenty of space to do small logic on the GUI thread. For instance, limiting the maximum number of spawned AsynTask
instances is as easily as well as correctly achievable by simply using some count variable. You can manipulate it safely from anywhere BUT inside your Do function. A good choice in this case will be the Pre function. Of course, you are welcomed to come with yet another breed of your custom AsyncTask
where such behavior is implemented from inside.
So, using async tasks in the latest C#5.0 language, I've managed to shorten the implementation quite a bit. On top of that, I've introduced canceling already improving my previous implementation feature wise.
Have I used the continuation concept in TPL, I could have come with even more concise implementation of AsyncTask
. Consider the following alternative:
this.task = Task.Factory.StartNew(() => Do(input, this),
cancelToken,
taskCreateOptions,
TaskScheduler.Default)
.ContinueWith<Tout>((t) => { return t.Result; }, cancelToken,
TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith<Tout>((t) => { Err(t.Exception); return default(Tout); },
cancelToken, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
.ContinueWith<Tout>((t) => { return t.Result; }, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith<Tout>((t) => { Err(t.Exception); return default(Tout); }, TaskContinuationOptions.OnlyOnFaulted);
The fact that our cancellation token is external and shared might not be desirable in case you plan to have multiple not related AsyncTask
s running. One obvious solution is to keep the token internal, specific for each instance. However, this will require keeping the actual instance of every AsyncTask
in a queue you will need to manage yourself. Something I consider outside the scope of this article.
Conclusions
After the latest TPL addition to the article, we have now two concrete implementation of the same Contract driven design solution to the commonly arising concern of how to decouple execution from GUI to the rest of your application. The original version, although more verbose, gives more classic C# solution and is easily deployable in any CLR 2.0 or later environment. On the other hand the new TPL AsyncTask
implementation is very compact, but it inherently brings more complexity. While the new async model in C# seems very easy and straightforward at first glance I found out that things are getting more complicated and convoluted below the surface. IMHO the exception handling needs extra attention. Canceling an sync tasks leaves even more space to be desired. To start, a cancellation is not a logic/flow exception and should not be handled through exceptions to start with. That is not entirely true as ultimately it is the good old pulling concept employed, which brings me to my final remark. You cannot really stop/abort an async tasks. That's right and you can experience yourself the affect I'm referring to if you experiment with the test form. Even after pressing the cancel button, any task which is already executing will continue so tile the end of its Thread.Sleep
function. This is not an issue particular to C#/CLR, but a rather general problem usually solved by pulling for cancellation request every so steps while running your long computation algorithm. However, the big difference is the abstraction of async tasks, which comes at the expense of control in this case. If I were using threads directly, I still have the option to prematurely aborting (kill) a thread. Arguably a good solution, but AFAIK works well as far as you don't mess some shared memory/state along the way. Oh.. and considering the last paragraph in the previous section, it truly makes me believe we need AsyncCancel
, since the GUI will freeze/unresponsive when you try canceling many task simultaneously.
Final Remarks
Use it on your own risk. The code in this article is entirely experimental and not production proof. In case you end up using it in production I will appreciate dropping a line and reflect on your experience.
[Added 2015] The reason against using TLP is the complicated to follow pattern of indirections:
Best wishes!