Click here to Skip to main content
Licence CPOL
First Posted 25 Jun 2009
Views 75,337
Downloads 544
Bookmarked 138 times

Avoiding InvokeRequired

By Pablo Grisafi | 29 Aug 2009
How to avoid asking if InvokeRequired has the minimum code and no copy/paste

1

2
2 votes, 3.2%
3
12 votes, 19.0%
4
49 votes, 77.8%
5
4.69/5 - 63 votes
2 removed
μ 4.65, σa 0.89 [?]

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 finally the simplest solution I've found. Wait to 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 that 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 has 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:

//No parameters
private void SetTextAnonymousDelegateMiniPattern()
{
    Invoke(new MethodInvoker(delegate
    {
    	this.text = "New Text";
    }));
}
//With parameters
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:

//No parameters
private void SetTextUsingPattern()
{
    this.UIThread(delegate
    {
    	this.text = "New Text";
    });
}
//With parameters
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();
    }
}

As you can see, it 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 (with just one extra line).
  • 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), generic type inference.

Bad points:

  • Er.... waiting for your comments.

Points of Interest

This code needs full .NET Framework 3.5 to work! To make it work in Compact Framework, simply declare MethodInvoker like:

public delegate void MethodInvoker();

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!

The download is a simple app that shows some wrong code that fails, the "standard pattern", and the "UIThread pattern" I've been talking about, and has two projects, one for full .NET Framework and one for Compact Framework.

History

  • 24th June, 2009: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Pablo Grisafi

Software Developer (Senior)
Globant
Argentina Argentina

Member
I'm a Java/C# developer, with (some) experience in mobile and web development. I enjoy learning useful (and useless) stuff to be a better developer. I like to share the (little amount of) knowledge I have, by creating libraries and utility classes. CodeProject rocks! I wonder why the Java part is not so popular...

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionHermano! Pinmemberalex.almeida6:21 28 Nov '11  
GeneralGreat article PinmemberCIDev10:50 25 May '11  
GeneralJust wondering Pinmembersage4510:16 22 Dec '10  
GeneralMy vote of 5 PinmemberShaneMcDonald6:17 15 Jul '10  
Questionthis.TextBox1.BeginInvoke(action)? PinmemberBaxterBoom23:00 14 Jul '10  
AnswerRe: this.TextBox1.BeginInvoke(action)? [modified] PinmemberPablo Grisafi8:48 25 Jul '10  
GeneralDisposed/Disposing form PinmemberEric A. Duesing10:48 31 Aug '09  
GeneralI would use the ISynchronizeInvoke interface PinmemberPaw Jershauge10:42 31 Aug '09  
GeneralMy general approach... Pinmembersupercat913:21 30 Aug '09  
Generalother way PinmemberMember 327145920:27 15 Jul '09  
GeneralRe: other way PinmemberSuperstom1:11 17 Jul '09  
That is a solution, but is not recommended:
http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/a62936aa-3abf-4787-bdd7-db1512c5e9ba
GeneralBetter Solution PinmemberIslam ElDemery23:28 30 Jun '09  
GeneralRe: Better Solution PinmemberGünther M. FOIDL4:25 6 Jul '09  
GeneralAnother 5 Vote with suggestion Pinmemberahmed zahmed10:27 30 Jun '09  
GeneralAnother method PinmemberPaul B.7:02 30 Jun '09  
GeneralGood article, not so good title.. Pinmembergglaze2:35 30 Jun '09  
GeneralRe: Good article, not so good title.. PinmemberPablo Grisafi6:53 30 Jun '09  
GeneralAlways use BeginInvoke instead of Invoke when applicable Pinmembertzach shabtay22:42 29 Jun '09  
GeneralMethodInvoker is redundant Pinmemberborlip22:30 29 Jun '09  
GeneralRe: MethodInvoker is redundant PinmemberPablo Grisafi6:59 30 Jun '09  
GeneralRe: MethodInvoker is redundant PinmemberRob Smiley12:37 30 Jun '09  
GeneralThanks Pinmembermburnie22:21 29 Jun '09  
Generalreturn Pinmemberrivariva21:39 29 Jun '09  
GeneralRe: return PinmemberPablo Grisafi7:17 30 Jun '09  
GeneralRe: return Pinmemberrivariva21:21 30 Jun '09  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120210.1 | Last Updated 29 Aug 2009
Article Copyright 2009 by Pablo Grisafi
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid