The vast majority of answers use Control.Invoke
which is a race condition waiting to happen. For example, consider the accepted answer:
string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate {
someLabel.Text = newText; // runs on UI thread
});
If the user closes the form just before this.Invoke
is called (remember, this
is the Form
object), an ObjectDisposedException
will be likely fired.
The solution is to use SynchronizationContext
, specifically SynchronizationContext.Current
as hamilton.danielb suggests (other answers rely on specific SynchronizationContext
implementations which is completely unnecessary). I would slightly modify his code to use SynchronizationContext.Post
rather than SynchronizationContext.Send
though (as there's typically no need for the worker thread to wait):
public partial class MyForm : Form
{
private readonly SynchronizationContext _context;
public MyForm()
{
_context = SynchronizationContext.Current
...
}
private MethodOnOtherThread()
{
...
_context.Post(status => someLabel.Text = newText,null);
}
}
Note that on .NET 4.0 and up you should really be using tasks for async operations. See n-san's answer for the equivalent task-based approach (using TaskScheduler.FromCurrentSynchronizationContext
).
Finally, on .NET 4.5 and up you can also use Progress<T>
(which basically captures SynchronizationContext.Current
upon its creation) as demonstrated by Ryszard Dzegan's for cases where the long-running operation needs to run UI code while still working.