Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the pattern to update UI from a different thread not built into the .NET framework?

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?

like image 764
Dinah Avatar asked Oct 23 '10 16:10

Dinah


1 Answers

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.

like image 167
Eric Lippert Avatar answered Sep 21 '22 02:09

Eric Lippert