LA.NET [EN]

Jun 22

In the last post, we’ve see that multithreading is almost a necessity in GUIs. We’ve also seen that there are some gotchas associated with it: a control can only be updated from the GUI thread. In practice, this means that we’ll need to marshal back the results to the main thread when they’re ready.

In .NET, the Windows Forms introduces the ISynchronizeInvoke interface for performing that kind of operation:

public interface ISynchronizeInvoke {
  IAsyncResult BeginInvoke(Delegate method, object[] args);
  object EndInvoke(IAsyncResult result);
  object Invoke(Delegate method, object[] args);
  bool InvokeRequired { get; }
}

The Control class (which is reused by all the existing controls) implements this interface, letting you marshal the results back to the main thread by calling one of the Invoke methods.

As you can see from the interface API, you can block the secondary thread until the GUI is updated (in this case, you use the Invoke method, which is equivalent to calling BeginInvoke followed by EndInvoke), or you can perform that work in asynchronous fashion, by invoking the BeginInvoke method and use one of the available approaches for waiting on the IAsyncResult returned. Besides these methods,the Control class offers two extra helper methods which you can use when you don’t need to pass parameters to the delegate:

public IAsyncResult BeginInvoke(Delegate method);
public object Invoke(Delegate method);

These are just shortcuts to the previous methods and don’t offer any benefits over the interface methods.

The InvokeRequired property is there for checking if marshalling is needed. After all,you don’t want to marshal if you don’t have to, right? Checking if marshalling is needed involves getting the Win32 control’s handle and seeing its window’s thread is the same as the current one.

Internally, the control class performs several interesting steps for executing the update on GUI through the Invoke methods(sync or async):

  • it starts by getting a reference to the control (or parent control) which has an associated Win32 handle;
  • it checks the associated thread ID (the unmanaged thread ID) and sees if marshaling will be needed (this happens whenever the control’s windows thread is different from the current thread that is calling the Invoke method – this relies in using the GetWindowThreadProcessId win 32 function;
  • it propagates the current ExecutionContext (more on this in future posts) so that it flows across threads;
  • if needed, it posts a message by using the Win32 PostMessage method (that’s why it needs to get a reference to a control that has a Win32 handle) and sets up a callback that will be fired when the message is processed.

As you’d expect, there’s a custom IAsyncResult implementation which performs many of the things we’ve seen before (like allocating a lazy event so that it is created only if it’s needed, etc, etc). To show you how easy it is to use marshalling, we’re going to create a Windows Forms test project which will use a secondary thread for calculating if a number is prime. We’ll start with a really simple form which has only one button for starting the operation:

frm

Here’s the code we’ve added to the buttons click event:

private void button1_Click(object sender, EventArgs e) {
  button1.Enabled = false;
  ThreadPool.UnsafeQueueUserWorkItem(state =>
  {
    var isPrime = CheckIfNumberIsPrime((Int32)state);
    Action updater = () => { 
MessageBox.Show(isPrime.ToString());
button1.Enabled = true; }; button1.Invoke(updater,
null); }, 19// hardcoded number ); }

As you can see, we’re making sure that all UI code runs on the GUI thread. The important thing here is making sure that the Enable property is set from the correct thread. In pre-NET 2.0, you could go ahead and set a property from a secondary thread. Most of the time, things would work and you’d get occasional crashes which were difficult to debug. From .NET 2.0 onward, the behavior changed: when you’re running in a debugger, you’ll always get additional checks which verify if the code is being called from the correct thread.

As a final optimization, you’ll probably want to stop the ExecutionContext from flowing in most GUI apps (specially for full trust apps). Doing this is as simple as calling the SuppressFlow method:

ExecutionContext.SuppressFlow();

And that’s it for today. Keep tuned for more on multithreading.

2 comments so far

  1. online physician phentermine
    8:19 pm - 8-2-2009

    aJk7JQ Perfect work!

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>