I know "why is my this framework like/not like xyz?" questions are a bit dangerous but I want to see what I'm missing.
In WinForms, you can't update the UI from another thread. Most people use this pattern:
private void EventHandler(object sender, DirtyEventArgs e)
{
if (myControl.InvokeRequired)
myControl.Invoke(new MethodInvoker(MethodToUpdateUI), e);
else
MethodToUpdateUI(e);
}
private void MethodToUpdateUI(object obj)
{
// Update UI
}
and more clever still is this pattern:
public static TResult SafeInvoke(this T isi, Func call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) {
IAsyncResult result = isi.BeginInvoke(call, new object[] { isi });
object endResult = isi.EndInvoke(result); return (TResult)endResult;
}
else
return call(isi);
}
public static void SafeInvoke(this T isi, Action call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired)
isi.BeginInvoke(call, new object[] { isi });
else
call(isi);
}
Regardless of which is used though, everyone has to write boilerplate code to handle this incredibly common problem. Why then, has the .NET Framework not been updated to do this for us? Is it that this area of the codebase is frozen? Is it a concern that it would break backwards compatibility? Is it a concern about the confusion when some code works one way in version N and a different way in version N+1?
I thought it might be interesting to mention why it is that there's a UI thread in the first place. It is to lower the cost of the production of UI components while increasing their correctness and robustness.
The basic problem of thread safety is that a non-atomic update of private state can be observed to be half-finished on a reading thread if the writing thread is halfway done when the read happens.
To achieve thread safety there are a number of things you can do.
1) Explicitly lock all reads and writes. Pros: maximally flexible; everything works on any thread. Cons: Maximally painful; everything has to be locked all the time. Locks can be contended, which makes them slow. It is very easy to write deadlocks. It is very easy to write code that handles re-entrancy poorly. And so on.
2) Allow reads and writes only on the thread that created the object. You can have multiple objects on multiple threads, but once an object is used on a thread, that's the only thread that can use it. Therefore there will be no reads and writes on different threads simultaneously, so you don't need to lock anything. This is the "apartment" model, and it is the model that the vast majority of UI components are built to expect. The only state that needs to be locked is state shared by multiple instances on different threads, and that's pretty easy to do.
3) Allow reads and writes only on the owning thread, but allow one thread to explicitly hand off ownership to another when there are no reads and writes in progress. This is the "rental" model, and it is the model used by Active Server Pages to recycle script engines.
Since the vast majority of UI components are written to work in the apartment model, and it is painful and difficult to make all those components free-threaded, you're stuck with having to do all UI work on the UI thread.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With