Introduction

Programming Windows Forms user interfaces is quite straightforward as long as you do not use multiple threads. But whenever your application has got some actual work to do, it becomes necessary to use threading to ensure the responsiveness of the UI. This is where Windows Forms programming can get quite complex.

The problem

As you know, Windows Forms is not thread safe in general. For example, it is not safe to get or set a property on a Windows.Forms control from any thread except the thread that handles the message queue. It is absolutely essential that you only make modifications to your Windows Forms controls from the message queue thread.

The standard solution

There is, of course, a mechanism to deal with this. Each Windows Forms control has the InvokeRequired property which returns false if the current thread is the message queue thread. And there is the Invoke method which makes it possible to enqueue a delegate complete with parameters into the message queue of the control.

Since the delegate is called directly from the message queue, no threading issues arise. But this style of programming can be quite tedious. Just to do something as simple as setting a text property or enabling/disabling a control, you have to define a separate method with a matching delegate.

Example: Random Strings

To illustrate this approach, I wrote a small Windows Forms program that generates random strings. Here is an excerpt of the code which shows how synchronization between the worker thread and the message loop thread is done.

I used Thread.Abort because in this case it is the simplest solution. If you do something that should not be interrupted under any circumstances, you should signal the thread using a flag instead.

This is a lot of simple but very repetitive code. Note that you always have to check InvokeRequired because calling Invoke before the message queue is created can lead to an error.

Generating thread-safe wrappers

In an earlier article, I showed how it is possible to automatically create wrappers for classes to make them "implicitly" implement an interface. The same code generation method can be extended to create wrappers that automatically ensure that methods are called in the right thread.

I will describe in detail how the whole thing works, later. First, let's take a look at how the mechanism is used.

First you expose the relevant properties in your form without considering threading issues. This is something you would probably want to do even if you would not use multithreading at all.

How it works

The wrapper generator uses System.Reflection.Emit to generate a proxy class that contains all methods required by the interface. This also includes property accessor methods that have a special signature.

The body of these methods will call the original method directly if InvokeRequired returns false. This is important to make sure that calling the methods does also work if the form is not yet attached to a message handling thread.

If InvokeRequired returns true, a delegate pointing to the original method is created and passed to the Invoke method of the form. Delegate types are cached so that you don't get multiple delegate types for the same method signature.

Since the wrapper generator uses the ISynchronizeInvoke interface for synchronizing invokes, you can also use it in a non-Windows-Forms application. All you would have to do is implement the interface and presumably implement some kind of message queue yourself.

Limitations and Caveats

It is important to understand that while the thread-safe wrapper hides the thread synchronization overhead, it does not make it go away. So accessing a property using a thread safe wrapper will be much slower than accessing it directly if InvokeRequired is true. So if you have to make multiple complex changes to your form from a different thread, it is best to make them all in one method instead of using separate property accessor calls.

Another thing to keep in mind is that not every kind of object can be safely passed from one thread to another without synchronization. In general, it is only safe to pass value types like int, DateTime etc. and immutable reference types like string. You have to be really careful with passing mutable reference types like StringBuilder from one thread to another. This can be OK if you are really sure that the object is not modified while a reference exists in different threads or if the object is thread safe. If in doubt, just pass a deep copy instead of a reference.

Whidbey

Whidbey will make multithreading issues easier since it has additional support for background processing in Windows Forms, and especially since it makes working with delegates much easier by supporting anonymous methods, which are really closures.

Setting a property in Whidbey is much simpler:

Invoke(delegate { labelStatus.Text="Working"; });

Getting a property as well:

int n=(int)Invoke(delegate { return numericUpDownDigits.Value; });

Unfortunately, Whidbey will probably be released bundled with Duke Nukem Forever in the year 2025.

References

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

Rüdiger Klaehn works as freelance developer in the space industry. He is very interested in functional programming languages and hopes that .NET will lead to a more widespread adoption in the industry.

I'm trying to create a thread wich set the image for an PictureBox(every 1000ms or more). Actually I've used a method very closlely to your method, but there is a problem when I minimize/maximize the form verry fast. The application crash. The method mentioned above works fine for simpler objects like TextBox. Please send the answer at "tpatrusel@yahoo.com". Thanks a lot.

This is certainly an interesting idea, and I see the rationale for it in some situations. In other cases, though, why don't you adopt the common practice of making the form's methods thread-safe by calling invoke from inside the method - for example (please excuse the formatting):

This has the following advantages:1. Reflection.Emit is not used, so lower CAS permissions are needed2. The thread problems are transparent to consumers

On the other hand, it has the following disadvantages:1. Each and every public method / property that may be accessed from another thread needs to have the "template" code at the start of this function.2. If a method has a signature that doesn't have an existing .NET delegate then the form will need to declare a delegate with the right signature. This delegate can be private, though, so we continue to hide the implementation from consumers - for example:

Pete Appleton wrote:This has the following advantages:1. Reflection.Emit is not used, so lower CAS permissions are needed2. The thread problems are transparent to consumers

I agree. In many situations it will be better to just write the few extra lines of code instead of having another dependency and requiring permission for Reflection.Emit. But I love doing stuff with Reflection.Emit, and since many of my programs require Reflection.Emit anyway, it is not such a big deal.

At the very least it is an interesting example of code generation using Reflection.Emit.

If you're already using Reflection.Emit, then my first point is indeed not valid. And I agree with you in that it's a very interesting and innovative way of using Reflection.Emit - also a well written and informative article which I enjoyed a lot.

Very well written article, interesting concepts to getting around threading issues.

I particularly liked this comment:

Unfortunately, whidbey will probably be released bundled with duke nukem forever in the year 2025

Haha come on now, next month (March 2005) Beta2 will be released with the GoLive license. I can't imagine it will be in beta for 20 more years. Come to think of it, I doubt MS will let the ship date slip past PDC this fall at the latest.

From what I've heard, virtually all the BCL is done, along with Visual Studio and the language changes. It's SQL Server 2005 that's holding things up...

Yep, and judging by the latest drop of SQL 2005, they've still got lots of issues to address in the Enterprise manager, not to mention the massive amount of memory needed and the occassional poorer performance when lined against SQL 2000.