The Windows Forms UI is not thread-safe, and most calls to WinForms components must be made from the thread that created them. Basically, every WinForms application has a UI thread and zero or more background threads. Control access must be via the UI thread. All well and good, but how do you accomplish this when dealing with background threads? As I see it, there's two main approaches.
Caller-driven marshalling
In this model, the code that is calling the component is responsible for any marshalling. A lot of library code is like this, pretty much everything using the BeginX...EndX pattern, such as System.IO.Stream. So, you can make your call to BeginRead, and pass in an AsyncCallback for the Stream to call when the read has completed. However, it's now up to you to take responsibility for marshalling that information back to your UI thread. The pattern I use in this situation is to call a method which will invoke itself to the UI thread:
private delegate void CompletionHandler(object result);
private void CompletionAction(object result)
{
if (!InvokeRequired)
{
// Do work
}
else
{
// We're in a background thread, so we call ourselves
// whilst marshalling to the UI thread
Invoke(new CompletionHandler(CompletionAction), result);
}
}
As you can see, this method can be called from UI and background threads with impunity and will automatically marshal itself to the UI thread.
A downside of caller driven synchronization is that it requires a level of understanding about threads that the people using the code may not have, which is why we come to:
Callee-driven marshalling
In this model the callee is responsible for marshalling the work back to the UI thread. If it's a control, this is easy, and you can just use the marshalling pattern I used above. If not, things become quite a bit more difficult. I'm a masochist, and tend to only use controls when I must have a UI. This, needless to say, makes my life more difficult. In .NET 2.0 life becomes a lot easier with the advent of the BackgroundWorker component. However, being a masochist, as mentioned before, I personally consider the BackgroundWorker bloated for anything other than the UI, so when I'm writing a threaded component, I instead use AsyncOperation which is actually how the BackgroundWorker component internally does what it does.
An AsyncOperation is created by a call to AsyncOperationManager.CreateOperation, and then used by calling Post and PostOperationCompleted. These methods marshal the call to the thread context in which CreateOperation was called. Note, not the thread, the context. So, for a Console application, where there is no thread affinity, these methods do no marshalling whatsoever, and I believe they also do nothing for ASP.NET. For WPF, again they should do nothing, but for WinForms they will marshal between Background and UI. Very cunning if you think about it, this is one pattern you can use to ensure that if there is thread-affinity you adhere to it, and if there isn't you don't suffer a big peformance hit, basically it just adds a virtual method call.
So, the pattern I use to take advantage of this is a bit more complex, but not difficult:
public class MyComponent : Component
{
private AsyncOperation _asyncOperation;
public void Start(string path)
{
_asyncOperation = AsyncOperationManager.CreateOperation(null);
// Kick off async operation, which will call FireAction
// when done
}
private void FireAction(string result)
{
// We're in the background thread, so now we'll marshal back
// to the main thread context
_asyncOperation.Post(new SendOrPostCallback(AsyncActionFired), result);
}
private void AsyncActionFired(object result)
{
// We're on the UI thread, but let's call a typed variant.
// Note that I make this method private since it is not
// type-safe, we don't want to expose what in theory could
// be corrupt parameters to inheritors
OnActionFired((string)result);
}
protected virtual void OnActionFired(string result)
{
// We're on the UI thread so fire the event or whatever
// Note that this method does NOT have to be private, since
// it's type-safe and on the right thread, you can allow
// inheritors access to it
}
}
For clarity I've left out OperationCompleted/PostOperationCompleted, but they're basically just called when you're finished whatever you were doing.
Wrapping up
So, on a few occasions, I've provided the entire gamut. I've supplied a class which handles threading in a caller-driven manner, a component which uses that class and exposes it's threading in a callee-driven manner, and a control which uses the component to fetch the information it will display. Overkill? Perhaps, but I can be damn sure that my code will support all levels of user: those using the code directly without threading, those just using the control or component and getting the threading for free, and those handling the marshalling themselves. The other nice thing is that such an approach also fits all levels of UI customisation, and I can support people who want to roll their own UI as well as people who just want to use my default UI.