CMSC 498B: Developing User Interfaces - Spring 2005

Threaded GUI Apps

Threaded GUI Apps

What are threads?  Conceptual simultaneous execution paths through your code.  Might be run through alternation or actually simultaneously (if on a multi-processor machine).
/*
 * A C# program that demonstrates simple use of threads
 *
 * Ben Bederson, March 9, 2005
 */
using System;
using System.Threading;

namespace ThreadSample {
    public class ThreadSample {
        int value;

        public ThreadSample(int value) {
            this.value = value;
        }

        public void Run() {
            do {
                Console.WriteLine("Thread " + value);
                Thread.Sleep(300);
            } while (true);
        }

        static public void Main() {
            ThreadSample sample1 = new ThreadSample(1);
            ThreadSample sample2 = new ThreadSample(2);
            Thread thread1 = new Thread(new ThreadStart(sample1.Run));
            Thread thread2 = new Thread(new ThreadStart(sample2.Run));
            thread1.Start();
            thread2.Start();
        }
    }
}

Source: ThreadSample.zip

What does this have to do with user interfaces?

  • All interface events are generated within a single special thread created by Windows Forms
  • Windows Forms is not "thread safe"  (what does this mean?)
  • The event dispatch thread is not the same as the main thread

=> You are not allowed to manipulate Forms objects outside the Event thread

=> Your own code needs to be thread safe if you want to modify your model outside of the Event thread, but access it within the Event thread.

=> From another thread, you can access the Event dispatch thread with Control.Invoke() and Control.BeginInvoke().

How to have slow event-driven operations?  What if they need to update the display?

...
counterThread = new Thread(new ThreadStart(IncrementCounter));
counterThread.Start();
...

// This gets called in a special thread
protected void IncrementCounter() {
    do {
        // Force update to happen in event thread
        BeginInvoke(new MethodInvoker(UpdateCounterDisplay));
        counterValue++;
        Thread.Sleep(100);
    } while (true);
}

protected void UpdateCounterDisplay() {
    counter.Text = counterValue.ToString();
}

Source:  ThreadedGUI.zip

Or, what if the GUI needs to be updated as a result of a non-GUI generated event (like network traffic)?

How is BeginInvoke() implemented?

C# Toolkits and Threads (from the QuickStart guide)

Windows Forms controls can only execute on the thread on which they were created, that is, they are not thread-safe. If you want to get or set properties, or call methods, on a control from a background thread, the call must be marshaled to the thread that created the control.

There are five functions on a control that are safe to call from any thread: InvokeRequired, Invoke, BeginInvoke, EndInvoke and CreateGraphics. For all other method calls, you should use one of the invoke methods.

By default, Windows marshals the calls for you. However, if you are making multiple calls to a control, it is much more efficient to create a method that executes those calls and make the one cross-thread call yourself. You make the cross-thread call by calling one of the Control.Invoke methods. The Invoke methods take a reference to a delegate. Typically, this delegate is an instance of the MethodInvoker delegate.

InvokeRequired public bool InvokeRequired { get ; }

Returns true if the caller must call Invoke when making method calls to this control.

BeginInvoke public IAsyncResult BeginInvoke(Delegate method)
public IAsyncResult BeginInvoke(Delegate method, Object[] args)

Executes the given delegate on the thread that owns this Control's underlying window handle. The delegate is called asynchronously and this method returns immediately. You can call this from any thread, even the thread that owns the control's handle. If the control's handle does not exist yet, this will follow up the control's parent chain until it finds a control or form that does have a window handle. If no appropriate handle can be found, BeginInvoke will throw an exception. Exceptions within the delegate method are considered untrapped and will be sent to the application's untrapped exception handler.

EndInvoke public Object EndInvoke(IAsyncResult asyncResult)
 

Retrieves the return value of the asynchronous operation represented by the IAsyncResult interface passed. If the async operation has not been completed, this function will block until the result is available.

 

Invoke public Object Invoke(Delegate method)
public Object Invoke(Delegate method, Object[] args)
 

Executes the given delegate on the thread that owns this control's underlying window handle. The delegate is called synchronously and this method returns once the invoked method has returned. The return value is the result of the invoked method. It is an error to call this on the same thread that the control belongs to.

Java Swing

Identical threading model for the UI

Invoke => SwingUtilities.invokeAndWait()
BeginInvoke => SwingUtilities.invoke();

Java's GUI toolkit.  Learn about it from the Java Tutorial:
http://java.sun.com/docs/books/tutorial/uiswing/index.html

The Swing Components from the Java Tutorial:
http://java.sun.com/docs/books/tutorial/uiswing/components/components.html

Learn more about Java and Threads from the Java Tutorial:
http://java.sun.com/docs/books/tutorial/uiswing/overview/threads.html