Hinweise zur Verwendung von Threads

Wenn Sie mit einem Widget-Toolkit arbeiten, müssen Sie das zu Grunde liegende Thread-Modell kennen, mit dem Ereignisse der Plattform-GUI gelesen und zugeteilt werden. Die Implementierung des UI-Threads hat Einfluss auf die Regeln, die Anwendungen bei der Verwendung von Java-Threads in ihrem Code befolgen müssen.

Native Ereigniszuteilung

Unterhalb einer GUI-Anwendung stellt die Betriebssystemplattform - unabhängig von ihrer Sprache oder dem UI-Toolkit - GUI-Ereignisse fest und stellt diese in die Ereigniswarteschlangen von Anwendungen. Obwohl sich die Mechanismen zwischen den einzelnen Betriebssystemen leicht unterschieden, sind die Grundlagen ähnlich. Sobald der Benutzer mit der Maus klickt, Zeichen eingibt oder in Fenstern navigiert, generiert das Betriebssystem GUI-Ereignisse wie Mausklicks, Tastatureingaben oder Fensterfüllungen. Es ermittelt, welches Fenster und welche Anwendung das jeweilige Ereignis empfangen sollte, und stellt das Ereignis in die Ereigniswarteschlange der Anwendung.

Die zu Grunde liegende Struktur jeder GUI-Anwendung mit Fensterfunktion ist eine Ereignisschleife. Anwendungen initialisieren und starten dann eine Schleife, die die GUI-Ereignisse einfach aus der Warteschlange liest und entsprechend reagiert. Alle Operationen zur Verarbeitung dieser Ereignisse müssen schnell ausgeführt werden, damit das GUI-System für den Benutzer empfangsbereit bleibt.

Lange Operationen, die durch GUI-Ereignisse ausgelöst werden, sollten in einem separaten Thread ausgeführt werden, damit der Thread der Ereignisschleife schnell zurückkehren und das nächste Ereignis aus der Warteschlange der Anwendung entnehmen kann. Der Zugriff auf die Widgets und Plattform-API aus anderen Threads muss jedoch durch explizites Sperren und eine serielle Anordnung gesteuert werden. Eine Anwendung, die diese Regeln nicht befolgt, kann einen Betriebssystemaufruf für das Fehlschlagen oder - schlimmer noch - das Sperren des gesamten GUI-Systems verursachen.

Toolkit-UI-Threads

Programmierer von nativen GUIs, die mit der Programmiersprache C arbeiten, sind mit den Überlegungen hinsichtlich des Entwurfs bei der Arbeit mit der Ereignisschleife der Plattform relativ vertraut. Höhere Widgets-Toolkits in Java versuchen jedoch häufig, die Anwendungsentwickler von den Problemen bei der Thread-Erstellung für Benutzerschnittstellen abzuschirmen, indem sie die Ereignisschleife der Plattform verdecken.

Dies wird häufig erreicht, indem ein dedizierter Toolkit-UI-Thread definiert wird, der aus der Ereignisschleife liest und zuteilt und die Ereignisse in eine interne Warteschlange stellt, die durch Anwendungen bedient wird, die in separaten Threads ausgeführt werden. Auf diese Weise kann das Toolkit in ausreichender Zeit Antworten an das Betriebssystem senden, wobei die Ablaufsteuerung der Anwendung bei der Verarbeitung des Ereignisses in keiner Weise eingeschränkt wird. Anwendungen müssen zwar weiterhin spezielle Sperrtechniken einsetzen, um aus ihrem Anwendungs-Thread auf UI-Code zuzugreifen, aber dies erfolgt im ganzen Code konsistent, weil der gesamte Anwendungscode in einem Nicht-UI-Thread ausgeführt wird.

Es erscheint zwar verlockend, Anwendungen vor UI-Thread-Problemen zu "schützen", aber in der Praxis können hierdurch viele Probleme verursacht werden.

Wenn die Ablaufsteuerung der GUI-Ereignisse von der Java-Thread-Implementierung und der Anwendungsleistung abhängig ist, wird es schwierig, Fehler zu diagnostizieren und zu beheben.

Moderne GUI-Plattformen führen viele Optimierungen mit Hilfe der Ereigniswarteschlange aus. Eine gängige Optimierung ist das Unterdrücken von aufeinander folgenden Ereignissen für Ausfüllungen in der Warteschlange. Immer dann, wenn ein Teil eines Fensters erneut gezeichnet werden muss, kann die Warteschlange auf überlagernde oder redundante Ausfüllereignisse hin überprüft werden, die noch nicht zugeteilt wurden. Diese Ereignisse können in einem gemeinsamen Ausfüllereignis gemischt werden, was eine seltenere Ausführung des Ausfüllcodes der Anwendung erforderlich macht. Diese Optimierung wird jedoch beeinträchtigt, wenn das Toolkit die Ereignisse schnell aus der Warteschlange extrahiert und in eine interne Warteschlange stellt.

Wen die Wahrnehmung des Anwendungsentwicklers für das Thread-Modell geändert wird, werden Programmierer verwirrt, die Erfahrungen mit der Programmierung des nativen GUI-Systems in anderen Sprachen und Toolkits haben.

SWT-UI-Thread

SWT folgt dem Thread-Modell, das durch die Plattformen direkt unterstützt wird. Das Anwendungsprogramm führt die Ereignisschleifen in seinem Haupt-Thread aus und teilt die Ereignisse direkt aus diesem Thread zu. Dies ist der UI-Thread der Anwendung.

Hinweis:  Technisch gesehen ist der UI-Thread der Thread, der die Anzeige erstellt. In der Praxis ist dies aber auch der Thread, der die Ereignisschleife ausführt und die Widgets erstellt.

Da der gesamte Ereigniscode vom UI-Thread der Anwendung ausgelöst wird, kann der Anwendungscode, der Ereignisse verarbeitet, frei auf die Widgets zugreifen und Grafikaufrufe ausgeben, ohne dass besondere Techniken erforderlich sind. Die Anwendung ist jedoch für die Aufspaltung der Berechungs-Threads zuständig, wenn infolge eines Ereignisses längere Operationen ausgeführt werden müssen.

Hinweis:  SWT löst für alle Aufrufe aus einem Nicht-UI-Thread, die aus dem UI-Thread erfolgen müssten, eine Ausnahmebedingung SWTException aus.

Der Haupt-Thread für eine SWT-Anwendung sieht (inklusive Ereignisschleife) wie folgt aus:

   public static void main (String [] args) {
      Display display = new Display ();
      Shell shell = new Shell (display);
      shell.open ();
      // Die Ereignisschleife starten. Der Stopp erfolgt, wenn der Benutzer etwas unternommen hat,
      // um unser Fenster zu entfernen.
      while (!shell.isDisposed ()) {
         if (!display.readAndDispatch())
            display.sleep();
      }
      display.dispose ();
   }

Sobald die Widgets erstellt wurden und die Shell geöffnet ist, liest die Anwendung Ereignisse aus der Warteschlange des Betriebssystem und teilt diese zu, bis das Shell-Fenster entfernt wird. Wenn keine Ereignisse in der Warteschlange vorhanden sind, wird eine Anweisung zur Inaktivierung der Anzeige ausgegeben, damit andere Anwendungen ausgeführt werden können.

Hinweise:  Das gängigste Thread-Modell für eine SWT-Anwendung ist die Ausführung eines einzigen UI-Threads und die Verwendung von Berechnungs-Threads für die Ausführung langer Operationen. SWT schränkt Entwickler jedoch nicht auf dieses Modell ein. Eine Anwendung könnte mehrere UI-Threads ausführen, die jeweils eine eigene Ereignisschleife enthalten.

SWT bietet spezielle Zugriffsmethoden, mit denen Widgets und Grafikcode von einem Hintergrund-Thread aus aufgerufen werden können.

Code aus einem Nicht-UI-Thread ausführen

Anwendungen, die UI-Code aus einem Nicht-UI-Code heraus aufrufen sollen, müssen eine ausführbare Ressource zur Verfügung stellen, die den UI-Code aufruft. Mit den Methoden syncExec(Runnable) und asyncExec(Runnable) in der Klasse Display werden diese ausführbaren Ressourcen zum entsprechenden Zeitpunkt im UI-Thread ausgeführt.

Der folgende Codeausschnitt veranschaulicht das Muster für die Verwendung dieser Methoden:

   // do time-intensive computations
   ...
   // Jetzt die UI. aktualisieren. Das Ergebnis muss nicht abgewartet werden,
   // daher asynchronen Modus verwenden.
   Display.getDefault ().asyncExec (new Runnable () {
      public void run() {
         myWindow.redraw();
      }
   });
   // Jetzt weitere Berechnungen ausführen.
   ...

Workbench und Threads

Die Thread-Regeln sind sehr eindeutig, wenn Sie eine SWT-Anwendung von Grund auf implementieren, weil Sie die Erstellung der Ereignisschleife und die Entscheidung über die Aufspaltung von Berechnungs-Threads in Ihrer Anwendung steuern.

Wenn Sie Plug-in-Code für die Workbench ergänzen, enthält der JFace- oder Workbench-Code keine "Zauber-Threads". Die Regeln sind ganz klar:

Copyright IBM Corporation und Andere 2000, 2003.