Introduction to Second Version
This is a new, extended, improved version of my original article. This was (and is) my first (and only until now) CodeProject article. It was well rated, but at the same time, a lot of people offered corrections, alternatives and improvements. So I will use all this new information to show you a better way of doing the same thing. Every improvement in my code was suggested by someone else, so this is sort of collective writing. I'm just compiling things that you can read in the forum. But since I don't always read the forum, it makes sense to me to make some "text oriented maintenance". I will try to give everyone his own credit.
There are two solutions now inside the download, one for VS2008 with four projects (Desktop and Compact Framework for C# 3.5 and C# 2.0) and another one for VS2005 with four projects (Desktop and Compact Framework for C# 2.0, two different ways to solve the problem for each).
At the very end of the article, you will find what I think now is the "official" way (the Microsoft way) to solve this problem. I didn't know about this method until a couple of people posted it in the forum. At least read that (click here). But I will stick to my way of solving this issue. The following text is pretty much the exact text from the original article; the new text begins below the second horizontal line. Enjoy!
Introduction
As you should already now, using Windows.Forms
gets really ugly when you need to access the user interface from multiple threads. IMHO, this is an example of leaky abstraction. I don't know (and I don't want to know) why I can't simply write:
this.text = "New Text";
In any thread, the Windows.Forms.Control class
should abstract me for any threading issue. But it doesn't. I will try to show several ways to solve this problem, and then finally, the simplest solution I've found. Wait until the end to find the good stuff (or click here)!
One thing worth knowing: When you run a program with this UI threading issue from within Visual Studio, it will always throw an exception. The same program running as a standalone EXE may not throw the exception. That is to say, the development environment is stricter than the .NET Framework. This is a good thing, it is always better to solve problems in development time that have random issues in production.
This is my first article and English is not my language, so please be gentle!
The "Standard" Pattern
I don't know who came out with this code at first, but this is the standard solution for the threading issue:
public delegate void DelegateStandardPattern();
private void SetTextStandardPattern()
{
if (this.InvokeRequired)
{
this.Invoke(new DelegateStandardPattern(SetTextStandardPattern));
return;
}
this.text = "New Text";
}
Good points about this solution:
- It does the job.
- It works with C# 1.0, 2.0, 3.0, 3.5, Standard and Compact Framework (since CF 1.1, you don't have InvokeRequired in CF 1.0).
- Everyone uses it, so when you read something like this, you know this code will be probably called from another thread.
Bad points:
- It's a lot of code for updating a text!
- You need to copy/paste it, you can't make a generic method from it.
- If you need to call a method with parameters, you can't even reuse the delegate. You need to declare another delegate for each different parameter set.
- It's ugly. I know this is subjective, but it is. I especially hate the need to declare a delegate "outside" the method.
There are some clever solutions out there, like this one using AOP, and this one using Reflection. But I wanted something easier to implement. One way to go could be a SurroundWith
code snippet, but I like my code issues to be solved by the language, not by the IDE. Also, it will only solve the copy/paste problem, it will still be a lot of code for something really simple.
Why can't we generalize the standard pattern? Because there is no way in .NET 1.0 to pass a block of code as a parameter, because when C# started, it had almost no support for a functional programming style.
The "Anonymous Delegate" Pattern
With C# 2.0, we get anonymous delegates and the MethodInvoker
class, so we could simplify the standard pattern into this:
private void SetTextAnonymousDelegatePattern()
{
if (this.InvokeRequired)
{
MethodInvoker del = delegate { SetTextAnonymousDelegatePattern(); };
this.Invoke(del);
return;
}
this.text = "New Text";
}
This is a slightly better solution, but I've never seen anyone using it. But what happens if instead of executing this.text = "New Text";
you need to call a method with parameters? Something like:
private void MultiParams(string text, int number, DateTime dateTime);
There is no big deal, since delegates can access outer variables. So, you can write something like this:
private void SetTextDelegatePatternParams(string text, int number, DateTime datetime)
{
if (this.InvokeRequired)
{
MethodInvoker del = delegate {
SetTextDelegatePatternParams(text, number, datetime); };
this.Invoke(del);
return;
}
MultiParams(text, number, datetime);
}
The "Anonymous delegate" pattern can be minimized a lot if you "forget" to ask if invoke is required. That leads us to...
The "Anonymous Delegate Minimized" Pattern
This is really good:
private void SetTextAnonymousDelegateMiniPattern()
{
Invoke(new MethodInvoker(delegate
{
this.text = "New Text";
}));
}
private void SetTextAnonymousDelegateMiniPatternParams
(string text, int number, DateTime dateTime)
{
Invoke(new MethodInvoker(delegate
{
MultiParams(text, number, dateTime);
}));
}
It works, it's easy to write, it's only a few lines away from perfect. The first time I saw this, I thought that's what I was looking for. So what's the problem? Well, we forgot to ask if Invoke
was required. And since this is not the standard way to do it, it will not be clear to others (or to ourselves in a couple of months) why we are doing this. We could be nice and comment the code, but let's be honest, we all know we won't. At least I prefer my code to be more "intention revealing". So, we have...
The "UIThread" Pattern, or the Way I've Solved this Problem
First, I show you the rabbit:
private void SetTextUsingPattern()
{
this.UIThread(delegate
{
this.text = "New Text";
});
}
private void SetTextUsingPatternParams(string text, int number, DateTime dateTime)
{
this.UIThread(delegate
{
MultiParams(text, number, dateTime);
});
}
And now, I'll show you the trick. It's a simple static
class with only one method. It's an extension method, of course, so if you have some objections like "extension methods are not pure object orientated programming", I recommend you to use Smalltalk and stop complaining. Or use a standard helper class, as you wish. Without comments, namespace and using, the class looks like this:
static class FormExtensions
{
static public void UIThread(this Form form, MethodInvoker code)
{
if (form.InvokeRequired)
{
form.Invoke(code);
return;
}
code.Invoke();
}
}
That was how far I've gone by myself. But then, I got the following suggestions from the developers in the forum:
- was333 said: Why just
Form
? Why not Control
? He was right. There's even a more abstract interface (ISynchronizeInvoke
) that Rob Smiley suggested, but I feel it is way too strange, and is not present in Compact Framework - Borlip pointed that
MethodInvoker
isn't present in CompactFramework
but Action
is, so it's more portable to use Action
- tzach shabtay has linked to this article pointing that it's better to use
BeginInvoke
than Invoke
when possible. Sometimes, that could be a problem, so we need two versions. But you should prefer BeginInvoke
.
So this is, until now, the final version:
static class ControlExtensions
{
static public void UIThread(this Control control, Action code)
{
if (control.InvokeRequired)
{
control.BeginInvoke(code);
return;
}
code.Invoke();
}
static public void UIThreadInvoke(this Control control, Action code)
{
if (control.InvokeRequired)
{
control.Invoke(code);
return;
}
code.Invoke();
}
}
You can use it this way:
this.UIThread(delegate
{
textBoxOut.Text = "UIThread pattern was used";
});
As you can see, is just the standard pattern, as generalized as possible. Good points about this solution:
- It does the job.
- It works the same with Full and Compact Framework.
- It's simple (almost looks like a using{} block!).
- It doesn't care if you have parameters or not.
- If you read it again in three months, it will still look clear.
- It uses a lot of what modern .NET has to offer: Anonymous delegates, extension methods, lambda expressions (if you want, see later).
Bad points:
- Er ... waiting for your comments. Again.
Points of Interest
You can write even less code using lambda style, if you only need to write one line, you can do something as small as:
private void SetTextUsingPatternParams(string text, int number, DateTime dateTime)
{
this.UIThread(()=> MultiParams(text, number, dateTime));
}
and still be clear! If you need to read from the Form
, you need to use UIThreadInvoke
, or you will find strange results.
private void Read()
{
string textReaded;
this.UIThreadInvoke(delegate
{
textReaded = this.Text;
});
}
But I'm pretty sure that if you are reading the screen from another thread, you are making a mistake somewhere.
For C# 2.0 and Visual Studio 2008
This code needs .NET Framework 3.5 to work. It works out of the box with both Desktop and Compact Framework. You have a working sample for both in the downloadable code. Some people asked about a .NET 2.0 version. There are two things from .NET 3.5 that we miss in 2.0:
Action
class: We have Action<T>
, but there is no simple-without-parameter-type Action
, because Action
is in System.Core.dll. That's easy, we just create the delegate inside System
namespace:
namespace System
{
public delegate void Action();
}
- Extension Methods: Thanks to Kwan Fu Sit who pointed to this article, there is a clever way to do that if you can use Visual Studio 2008. Since Extension methods are just a compiler trick, the only thing you need to add to your project is a new class:
namespace System.Runtime.CompilerServices
{
[AttributeUsage
(AttributeTargets.Method|AttributeTargets.Class|AttributeTargets.Assembly)]
public sealed class ExtensionAttribute : Attribute
{
}
}
and that's it! It's very useful, not only for this UIThread
trick. I've added both ExtensionAttribute
and Action
in the same file CSharp35Extras.cs. Check the details in the respective projects of the same solution. Once again, the exact same code works in both Desktop and Compact framework.
For C# 2.0 and Visual Studio 2005
I've found basically three ways to make it work in VS2005 and none of them are very elegant. In all of them, I use MethodInvoker
instead of Action
because MethodInvoker
is present in the desktop .NET Framework 2.0. You still need to declare the MethodInvoker
class somewhere if you work in Compact Framework. For simple one-window-project, (or one-window-with-multithreading-issues, to be precise), just copy a method inside the FormWathever.cs.
private void UIThread(MethodInvoker code)
{
if (this.InvokeRequired)
{
this.BeginInvoke(code);
return;
}
this.Invoke();
}
You can use it like this:
UIThread(delegate
{
textBoxOut.Text = "UIThread pattern was used";
});
I think this is a good enough solution for simple projects. But when you have the same problem in a second window and you start copy/pasting the method, it's not so good.
Another option is to create a helper class like this:
static public class UIHelper
{
static public void UIThread(Control control, MethodInvoker code)
{
if (control.InvokeRequired)
{
control.BeginInvoke(code);
return;
}
control.Invoke();
}
}
And then invoke the UIThread
like this:
UIHelper.UIThread(this, delegate
{
textBoxOut.Text = "New text";
});
I have no problem having a UIHelper
class, I always end up using a UIHelper
class for one reason or another, but I don't like the UIHelper.UIThread(this,...
part. It's too verbose to me. But it works, and at least you are not copy/pasting code.
Another way is to create a FormBase
class like this:
public class FormBase : Form
{
public void UIThread(MethodInvoker code)
{
if (this.InvokeRequired)
{
this.BeginInvoke(code);
return;
}
code.Invoke();
}
}
then inherit all your forms from FormBase, and then invoke like this:
UIThread(delegate
{
textBoxOut.Text = "New text";
});
The invoking part is fine, but I don't enjoy inheriting all my forms from FormBase
, specially because sometimes, when I am using visual inheritance, and I switch to design mode, Visual Studio shows me really horrible screens like this one:
(Regarding this problem, the only solution I know when it happens is to close all Design tabs, then Build-Clear Solution, then close Visual Studio, then delete all files under bin and obj folders, reopen Visual Studio and Rebuild Solution and then reopen the FormWhatever
in design view)
You are also losing the Control
generalization; this way only works for Form
s. Is up to you to choose one of these partial solutions, or migrate to VS2008, or to put pressure in your boss to migrate to VS2008. Came on! VS2010 is just around the corner.
For C# 1.0 and Visual Studio 2003
Are you kidding me? (I mean, I don't have a solution for that environment, and I don't think it's possible.)
Alternatives
When I wrote the first version of this article, I was aware of some alternatives to avoid copy/pasting code. I didn't like any of them, that was my motivation, but I listed those alternatives at the beginning of the article in order to show them to everyone. However, there was another alternative that I didn't think about: Alomgir Miah A suggested to use BackgroundWorker
. I think it's too verbose, and it doesn't exist in Compact Framework. But sure, it exists in the full framework, and it can be used to avoid threading issues.
Two people suggested to use Control.CheckForIllegalCrossThreadCalls = false;
. DON'T DO THAT! That is terribly wrong! From the MSDN documentation: illegal cross-thread calls will always raise an exception when an application is started outside the debugger.
.. Setting Control.CheckForIllegalCrossThreadCalls = false;
will only disable the debugger capability to detect all possible threading issues. So, you may not detect them when debugging, but when your app is running you may have horrible exceptions killing your app and have never been able to reproduce them. You are choosing to close your eyes when crossing the street. It's easy to do, but risky. Again, DON'T DO THAT!. Use whatever solution works for you, never ever write Control.CheckForIllegalCrossThreadCalls = false;
.
The "Official" Pattern
Finally, two other people (Islam ElDemery and Member 170334, who has no name and no friendly URL) showed me what I think is the official solution that Microsoft has developed for this problem, and so it's probably better than mine: The SynchronizationContext
class. I have to admit I didn't know about that, and it's been available since .NET Framework 2.0! It can be used very much like my own solution, and it's probably faster, since it's included in the framework, and it offers you more options. I am adult enough to show this solution here, even when it makes my own work pretty useless, and I am kid enough to reject it later. It's a two step solution: First, you need a SynchronizationContext
member that must be initialized inside the constructor:
class FormWathever
{
private SynchronizationContext synchronizationContext ;
public FormWathever()
{
this.synchronizationContext = SynchronizationContext.Current;
}
}
and then, when you need to do some thread-unsafe form actualizations, you should use something like this:
synchronizationContext.Send(new SendOrPostCallback(
delegate(object state)
{
textBoxOut.Text = "New text";
}
), null);
It works, it's incorporated into the framework, I'm sure it's fast, and has some extra options. Why not to use it? I only can think in four reasons, and not very good ones. They look more like excuses.
- I don't like the initialization part. I don't understand why Microsoft didn't include a
SynchronizationContext
property inside the Form
class, automatically initialized in the base constructor. In order to skip initialization by yourself, you need to inherit all your forms from a FormBase
or something like this. - It's kind of verbose. You need to create that
SendOrPostCallback
object, and pass that extra null
parameter, and the extra object
state. You could avoid this extra work by using another helper method, but in this case, I'll stick to UIThread
. - It's not "intention revealing code". And since it's not very popular, it makes your code harder to understand and maintain by others. (But not too much, let's be honest.)
- It doesn't exist in Compact Framework.
But if you don't need to care about Compact Framework, and you think that some extra typing will not kill you, that's probably the way to go.
History
- 24th June, 2009: Initial version
- 29th August, 2009: Extended version, several improvements, support for C# 2.0, some support for VS2005, and the "Official" pattern