QueueUserWorkItem
would technically work, but is extremely low-level. There are easier ways.
I recommend using the new Task
feature of .NET 4.0. It does exactly what you want, including synchronizing the result or error conditions to another thread (the UI thread, in this case).
If .NET 4.0 is not an option, then I'd recommend either BackgroundWorker
(if your background processing is not too complex), or asynchronous delegates such as Hans mentioned. If you use async delegates, then use the AsyncOperation
class to marshal the results back to the UI thread.
The Task
option is very nice because it handles parent/child tasks very naturally. BackgroundWorker
can't be nested. Another consideration is cancellation; Task
and BackgroundWorker
have built-in support for cancellation, but for async delegates you'd have to do your own.
The only place where Task
is a bit more complex than BackgroundWorker
is in progress reporting. It's not quite as easy as BackgroundWorker
, but I have a wrapper on my blog to minimize that.
To summarize, in order of preference:
Task
- supports proper marshaling of errors, the concept of a result, cancellation, and parent/child nesting. Its one weakness is that progress reporting isn't simple (you have to create another Task and schedule it to the UI thread).
BackgroundWorker
- supports proper marshaling of errors, the concept of a result, cancellation, and progress reporting. Its one weakness is that it doesn't support
parent/child nesting, and that limits its usage in APIs, e.g., for a business layer.
Delegate.BeginInvoke
with AsyncOperation
- supports proper marshaling of erros, the concept of a result, and progress reporting. However, there is not a built-in concept of cancellation (though it can be done by hand using a volatile bool
). It also does not support parent/child nesting.
Delegate.BeginInvoke
with SynchronizationContext
- this is the same as option (3) except it uses SynchronizationContext
directly. The code is slightly more complex, but the tradeoff is that parent/child nesting is supported. All other limitations are identical to option (3).
ThreadPool.QueueUserWorkItem
with AsyncOperation
or SynchronizationContext
- supports the concept of progress reporting. Cancellation suffers from the same problem as option (3). Marshaling of errors is not easy (in particular, preserving the stack trace). Also, parent/child nesting is only possible if the SynchronizationContext
is used instead of AsyncOperation
. Furthermore, this option does not support the concept of a result, so any return value(s) need to be passed as arguments.
As you can see, Task
is the clear winner. It should be used unless .NET 4.0 is not an option.