The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner. - ISerializable - Roy Osherove's Blog

The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

With .NET 2.0 and with the new "anonymous delegates" feature in C#, doing multi threaded responsive GUIs is now easier. In fact, you get multiple choices on how to accomplish this task. But which choice is the best? and what does "best" really mean? more elegant? faster? more readable? all of the above?

Here's a little scenario for you. Assume there is a method that displays a string on the GUI, but may be called by a different thread than the GUI. The method has only a single string parameter. here are three versions of how you can implement this method and keep it thread safe. The last one is my favorite.

First, I'll declare two "generic" delegates. Each one is used in a different version of the method.

 delegate void Func<T>(T t);
private void displayError(string message)
        {
            
if (InvokeRequired)
            {
                Func<
string> del displayError;
                
Invoke(del, message);
                return;
            
}
            errorDisplay.Show(message)
;
        
}
This version uses a generic delegate which takes a string argument, then if Invoke is needed, it "Invokes" the delegate passing in the string argument. Note that passing in the string is not a strongly typed operation and may result in runtime errors if we make a mistake in the parameter order for example. That's why I don't like this version at all. But it's the most commonly used that I've seen, which is why I'm writing this.


delegate void Func();
        private void displayError1(string message)
        {
            
if (InvokeRequired)
            {
                Func del 
= delegate { displayError1(message)};
                
Invoke(del);
                return;
            
}
            errorDisplay.Show(message)
;
        
}
        
This version uses a delegate with no parameters, and uses the "delegate" directive to create a delegate that calls the same method with the "message" parameter. Notice how lovely it is to be able to use a local scoped variable such as "message" in out delegate. It makes life much simpler because now all we ever need to do is just use one delegate signature for all of our thread-safe GUI work. This version is much better than the previous one because it's type-safe. We can't make a mistake in parameter order or type, or we'll have a build error. But this version is still not as elegant as I would like.


        private void displayError2(string message)
        {
            Func del 
= delegate
                
{
                    errorDisplay.Show(message)
;
                
};
            
Invoke(del);
        
}
This version uses the delegate directive to actually create the full functionality of our method. Notice that we *always* invoke the code in here using the "Invoke" method which is thread-safe, even if this is not required. Also note how much less code we have to write here, and that we always use the same delegate signature to accomplish this. Not only that, in contrast to the other two implementations, we only write the calling code once inside the delegate, and not another time outside of it which can lead to maintenance problem due to duplication. This method, to me, is the most elegant and readable. I don't really care about one extra call to the "Invoke" method, since this method is GUI related, and obviously is called after a long operation which was async. 10 nano seconds more or less won't matter one bit. This is the method I'd encourage you to use, even though it seems a bit un-orthodox at first, it is clearly the simplest of the three, with less code, less duplication and more readability.


Colorized by: CarlosAg.CodeColorizer

Unfortunately, all three methods require you to use C#, since anonymous delegates are not present in VB.NET yet.

Thoughts?

Published Wednesday, March 01, 2006 4:13 AM by RoyOsherove
Filed under:

Comments

Wednesday, March 01, 2006 8:19 AM by Urs

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

hm, at least the first example is perfectly possible in VB.NET 2005:

Private Delegate Sub Func(Of T)(ByVal parm As T)

Private Sub DisplayError(ByVal message As String)
If InvokeRequired Then
Dim del As New Func(Of String)(AddressOf DisplayError1)
del.Invoke(message)
Return
End If

ErrorDisplay.Show(message)
End Sub

AFAIK, there are no anonymous delegates involved here, right?
Best regards,
Urs

Wednesday, March 01, 2006 10:23 AM by Jonathan de Halleux

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Roy, your formatting is a little weird???
Wednesday, March 01, 2006 11:28 AM by Roy Osherove

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Jonathan, how do you mean? looks fine in IE..
Wednesday, March 01, 2006 11:29 AM by Roy Osherove

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Urs: yes. You're right. I should have said "The last two" are not possible, not all of them. However, I don't like the furst way anyway, and was just using it as a jumping step to the better examples.
Thanks for the comment.
Roy.
Wednesday, March 01, 2006 1:24 PM by Stef Rave

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

You can also use the ThreadStart delegate from Stystem.Threading:

private void displayError2(string message)
{
Invoke(new ThreadStart(delegate
{
errorDisplay.Show(message);
}));
}

I saw that trick on Clemens Vasters blog
http://friends.newtelligence.net/clemensv/PermaLink,guid,4b9d14f4-a6e3-454b-bcaf-35cbd903d393.aspx
Friday, March 03, 2006 9:08 AM by Tony Day

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Hi Roy

Thanks for the article.

At the risk of exposing my lack of understanding I would like to ask the following :

In your description of the third (and preferred) option you describe an 'extra call to the "Invoke" method'.
This seems to suggest that a call to "displayError2" may result in more than one call to the "Invoke" method.
Is this was you meant ??
Or did you you mean (as I interpreted it) that there will be an unecessary call to "Invoke" if "displayError2" is called by the main GUI thread ??

Sorry if this seems pedantic but your answer will confirm that I have in fact understood (or not :-)

Regards, Tony
Saturday, March 04, 2006 5:24 AM by Roy Osherove

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Tony: I meant that that there will be an unecessary call to "Invoke" if "displayError2" is called by the main GUI thread.
Saturday, March 04, 2006 11:40 AM by Samboy LIms

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Roy,

Is the No.3 approach actually used by Microsoft at all? Is this pattern present in any of their C# examples?

Sam
Saturday, March 04, 2006 12:49 PM by Roy Osherove

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Sambo - Haven't seen this used anywhere in the docs or samples. Surprised?
Monday, March 06, 2006 2:43 AM by Tony Day

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Hi Roy.

Thanks for the confirmation.

Regards, Tony.
Wednesday, March 08, 2006 8:51 AM by Ian Griffiths

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Why are you using Invoke as opposed to BeginInvoke?

Invoke makes the non-UI thread wait until the UI thread has finished calling your method.

This is more prone to deadlock than BeginInvoke, which doesn't block. E.g., if your UI thread and worker thread ever use some shared resource, then that will need to be protected by some kind of locking mechanism. If your worker thread is in posession of that lock at the point at which it calls Invoke (easily done if you ever end up calling Invoke inside of an event handler - who knows what locks are being held by whatever thread called your event handler?) and the UI thread was trying to acquire the lock, you've just frozen your process terminally.

With Control.BeginInvoke, this situation never arises.

Always prefer BeginInvoke. If you have a really good reason for wanting to use Invoke instead, then fix your design so you can use BeginInvoke instead. :) If you really can't do that, then consider using Invoke, but put big flashing warning comments around it indicating that it's a potential deadlock hazard, and needs very close attention from anyone modifying the code in the future.
Monday, March 13, 2006 7:01 AM by Roy Osherove

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Ian - thanks for writing this comment. It certainly is something I had not considered as a problem, but will certainly have to consider it once more.
I'm not inclined to use BeginInvoke mainly because it really does do its work in an async manner.
Plenty of times when you do GUI code, you're assumign that the GUI code is indeed blocking.
But, that's just a feeling,. I'll check it out more seriously and write down what I find.
Thursday, May 25, 2006 1:20 AM by john B

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Sorry about the late comments but what about this method.

private void displayError2(string message)
{
 Invoke(new MethodInvoker(
   delegate
   {
     errorDisplay.Show(message);
   }
   ));
}

This has the added benefit of removing the need for the delegate declaration.

Cheers
JB
Thursday, May 25, 2006 4:02 PM by HL

# re: The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

Has anyone figured out the thread management related to data binding? I have seen many posts on using the Invoke method to directly update controls, but I have not seen antyone delve into the internals of what threads are executing when a data bound property on an object changes. In my testing it appears that the data binding related events are occurring on the background thread, at least through the BindingManagerBase CurrentChanged Event. Note that the control events (TextCHanged, Invalidated) occur on the correct thread.

HL
Saturday, August 19, 2006 8:47 AM by Nblog (a shadow copy)

# Thread-Safe GUI

Roy Osherove na swoim blogu pokazuje sposoby na implementację metod dostępowych do elementów okna w Windows...

Sunday, January 21, 2007 8:57 AM by Tim Van Wassenhove » About Thread-Safe GUI…

# Tim Van Wassenhove &raquo; About Thread-Safe GUI&#8230;

# Thead Safe Calls To Windows Forms in Visual Studio 2005 at clifgriffin &gt; blog

Pingback from  Thead Safe Calls To Windows Forms in Visual Studio 2005 at  clifgriffin &gt; blog

Monday, February 04, 2008 8:17 PM by egeye

# .NET 2.0 中,两种如何创建一个线程安全的 UI 的方法

刚刚编写了线程安全的代码,该方法示例执行 Windowns Forms Event Handler 跨线程 UI 调用,BindXmlDocumentToTreeView 是一个绑定 Xml 到一个 TreeView 控件的方法,其可以很好的工作于不同的线程。它首先检查 Control(TreeView)的 InvokeRequired 属性,以判断是否是与 UI 线程不同的线程调用,然后使用 TreadPool 工具,并借助 .Net 2.0 中的匿名代理,使用 TreadStart 委托。

Wednesday, April 02, 2008 2:40 AM by zyly2

# 一个多线程图片抓取.net类库的实现(一)

其实现在网上内容抓取的工具和代码也比较多了,我查了看了看,网上很多都是讲述如何利用xmlhttp(ajax)方式来对网页的内容来进行抓取,因为利用javascipt很容易对抓取的内容进行分析。