SWT线程
Threading issues
When working with a widget toolkit, it is important to understand the underlying thread model that is used for reading and dispatching platform GUI events. The implementation of the UI thread affects the rules that applications must follow when using Java threads in their code.
Native event dispatching
Underneath any GUI application, regardless of its language or UI toolkit, the OS platform detects GUI events and places them in application event queues. Although the mechanics are slightly different on different OS platforms, the basics are similar. As the user clicks the mouse, types characters, or surfaces windows, the OS generates application GUI events, such as mouse clicks, keystrokes, or window paint events. It determines which window and application should receive each event and places it in the application's event queue.
The underlying structure for any windowed GUI application is an event loop. Applications initialize and then start a loop which simply reads the GUI events from the queue and reacts accordingly. Any work that is done while handling one of these events must happen quickly in order to keep the GUI system responsive to the user.
Long operations triggered by UI events should be performed in a separate thread in order to allow the event loop thread to return quickly and fetch the next event from the application's queue. However, access to the widgets and platform API from other threads must be controlled with explicit locking and serialization. An application that fails to follow the rules can cause an OS call to fail, or worse, lock up the entire GUI system.
Toolkit UI threads
Native GUI programmers using C are quite familiar with the design considerations for working with the platform event loop. However, higher level widget toolkits in Java often attempt to shield application developers from UI threading issues by hiding the platform event loop.
A common way to achieve this is to set up a dedicated toolkit UI thread for reading and dispatching from the event loop, and posting the events to an internal queue that is serviced by applications running in separate threads. This allows the toolkit to respond in sufficient time to the operating system, while not placing any restrictions on the application's timing in handling the event. Applications must still use special locking techniques to access UI code from their application thread, but it is done consistently throughout the code since all application code is running in a non-UI thread.
Although it sounds tempting to "protect" applications from UI threading issues, it causes many problems in practice.
It becomes difficult to debug and diagnose problems when the timing of GUI events is dependent on the Java threading implementation and application performance.
Modern GUI platforms perform many optimizations with the event queue. A common optimization is to collapse consecutive paint events in the queue. Every time that part of a window must be repainted, the queue can be checked for overlapping or redundant paint events that have not been dispatched yet. These events can be merged into one paint event, causing less flicker and less frequent execution of the application's paint code. This optimization will be defeated if the widget toolkit is pulling the events off the queue quickly and posting them to an internal queue.
Changing the developer's perception of the threading model causes confusion for programmers that have experience with programming the native GUI system in other languages and toolkits.
SWT UI thread
SWT follows the threading model supported directly by the platforms. The application program runs the event loop in its main thread and dispatches events directly from this thread. This is the application's "UI thread."
Note: Technically, the UI thread is the thread that creates the Display. In practice, this is also the thread that runs the event loop and creates the widgets.
Since all event code is triggered from the application's UI thread, application code that handles events can freely access the widgets and make graphics calls without any special techniques. However, the application is responsible for forking computational threads when performing long operations in response to an event.
Note: SWT will trigger an SWTException for any calls made from a non-UI thread that must be made from the UI thread.
The main thread, including the event loop, for an SWT application looks like the following:
public static void main (String [] args) { Display display = new Display (); Shell shell = new Shell (display); shell.open (); // start the event loop. We stop when the user has done // something to dispose our window. while (!shell.isDisposed ()) { if (!display.readAndDispatch ()) display.sleep (); } display.dispose (); }
Once the widgets are created and the shell is opened, the application reads and dispatches events from the OS queue until the shell window is disposed. If there are no events available for us in the queue, we tell the display to sleep to give other applications a chance to run.
Note: The most common threading model for an SWT application is to run a single UI thread and perform long operations in computational threads. However, SWT does not restrict developers to this model. An application could run multiple UI-threads with a separate event loop in each thread.
SWT provides special access methods for calling widget and graphics code from a background thread.
Executing code from a non-UI thread
Applications that wish to call UI code from a non-UI thread must provide a Runnable that calls the UI code. The methods syncExec(Runnable) and asyncExec(Runnable) in the Display class are used to execute these runnables in the UI thread at an appropriate time.
- syncExec(Runnable) should be used when the application code in the non-UI thread depends on the return value from the UI code or otherwise needs to ensure that the runnable is run to completion before returning to the thread. SWT will block the calling thread until the runnable has been run from the application's UI thread. For example, a background thread that is computing something based on a window's current size would want to synchronously run the code to get the window's size and then continue with its computations.
- asyncExec(Runnable) should be used when the application needs to perform some UI operations, but is not dependent upon the operations being completed before continuing. For example, a background thread that updates a progress indicator or redraws a window could request the update asynchronously and continue with its processing. In this case, there is no guaranteed relationship between the timing of the background thread and the execution of the runnable.
The following code snippet demonstrates the pattern for using these methods:
// do time-intensive computations ... // now update the UI. We don't depend on the result, // so use async. display.asyncExec (new Runnable () { public void run () { myWindow.redraw (); } }); // now do more computations ...
The workbench and threads
The threading rules are very clear when you are implementing an SWT application from the ground up since you control the creation of the event loop and the decision to fork computational threads in your application.
If you are contributing plug-in code to the workbench, there is no threading "magic" hidden in the JFace or workbench code. The rules are straightforward:
- Your workbench plug-in code executes in the workbench's UI thread.
- If you receive an event from the workbench, it is always executing in the UI thread of the workbench.
- If your plug-in forks a computational thread, it must use the Display asyncExec(Runnable) or syncExec(Runnable) methods when calling any API for the workbench, JFace, or SWT.
- Workbench and JFace API calls do not check that the caller is executing in the UI thread. However, SWT triggers an SWTException for all API calls made from a non-UI thread.
- If your plug-in uses the JFace IRunnableContext interface to invoke a progress monitor and run an operation, it supplies an argument to specify whether a computational thread is forked for running the operation.