Monday, October 8, 2012

Invoking on the UI thread in GTK#

Not Threadsafe
GTK like most GUI toolkits is not thread safe. This means that you must only ever update it form the UI thread. If we want our applications to be responsive however we must run any long running processing on a different thread so that the UI thread is free to respond to user input. This posses a problem because we need to be able to update the UI to reflect the progress and outcome of the processing, but we can't do that directly because the UI is not threadsafe.

How to update from another thread without updating from another thread
Fortunitely GTK# has a built in mechanizm for running code on the UI thread from another thread. This mechanism is call Application.Invoke and you can call it like this.


Application.Invoke((_,__) =>
{
    // update UI here
});


This method however is suboptimal, firstly because Application.Invoke takes two parameters (object sender, EventArg args). In most cases we don't need these and so in my example I have used _ for the sender and __ for the args. The second problem is that we only really need to call this if we are not already on the UI thread. We don't want to take on the penalty of the overhead if we are already on the UI thread.

There must be a better way
In winforms useful methods are provided which solve the problems presented by Application.Invoke.

The first is Control.InvokeRequired this will return true if we are not on the UI thread or false if we are.

The second is Control.Invoke this is like Application.Invoke but without the overhead of having to specify a sender and args and can be called like this:

Control.Invoke(()  =>
{
    //update UI here
});

By combining these you can write code that only invokes when it needs to. The details of this can be found here:
http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx

Can we do that in GTK?
Yes we can. We can achive exactly that using Extension Methods. Actually we can improve on it by making the Invoke method only call Application.Invoke if we are not already on the UI thread. The following code is provided under the Unicorn licence.

Doing this means that what was:

Application.Invoke((_,__) =>
{
    // update UI here
});

Becomes:

Invoke(()  =>
{
    //update UI here
});

Which is cleaner and takes care of figuring out what thread your are on for you.

2 comments:

Luís Reis said...

Will this really work? It seems to me that the first call to InvokeRequired will always return true even if we are not in the UI thread.
Therefore, if we make the first call to InvokeRequired from outside the UI thread, this won't work at all, right?

Daniel Hughes said...

Yes this does work. The very first call will invoke regardless. The UI thread is set from inside the Application.Invoke call so it will always be the UI thread.