Call WinForms on Multiple Threads
Use the ISynchronizeInvoke interface to marshal calls to the correct thread, and put HTML on the clipboard that other apps can use.
by Juval Löwy and Karl E. Peterson
February 2003 Issue
Technology Toolbox: C#, VB6, VB5, WinForms
Q: Call WinForms on Multiple Threads
My WinForms application has a worker thread that updates the main windows. The documentation warns against calling the form on multiple threads (why?), and indeed, it crashes occasionally if I do. How can I call methods on the form from multiple threads?
A:
Every WinForms class that derives from the Control class (including Control) relies on the underlying Windows messages and on a message pump loop to process them. The message loop must have thread affinity, because messages to a window are delivered only to the thread that creates it. As a result, you can't call message-handling methods from multiple threads, even if you provide synchronization. Most of the plumbing is hidden from you, because WinForms use delegates to bind messages to event-handling methods. WinForms convert the Windows message to a delegate-based event, but you still must be aware that only the thread that creates the form can call its event-handling methods, because of the primordial message loop. If you call such methods on your own thread, they'll execute on it instead of on the designated form thread. You can call any methods that you know aren't message handlers (such as your own custom methods) from any thread.
The Control class (and the derived classes) implement an interface defined in the System.ComponentModel namespace—ISynchronizeInvoke—to address the problem of calling message-handling methods from multiple threads:
public interface ISynchronizeInvoke
{
object Invoke(Delegate
method,object[] args);
IAsyncResult BeginInvoke(Delegate
method,object[] args);
object EndInvoke(IAsyncResult
result);
bool InvokeRequired {get;}
}
ISynchronizeInvoke provides a generic, standard mechanism to invoke methods on objects residing on other threads. For example, the client on thread T1 can call ISynchronizeInvoke's Invoke() method on an object if the object implements ISynchronizeInvoke. The implementation of Invoke() blocks the calling thread, marshals the call to T2, executes the call on T2, marshals the returned values to T1, then returns control to the calling client on T1. Invoke() accepts a delegate targeting the method to invoke on T2, and a generic array of objects as parameters.
The caller can also check the InvokeRequired property, because you can call ISynchronizeInvoke on the same thread as the one the caller tries to redirect the call to. The caller can call the object methods directly if InvokeRequired returns false.
Back to top
|