Zagadnienia związane z wątkami

Korzystając z zestawu narzędzi do obsługi widgetów, należy w dostatecznym stopniu znać model wątków stosowany do odczytywania i rozsyłania zdarzeń dotyczących graficznego interfejsu użytkownika platformy. Implementacja wątku interfejsu użytkownika znajduje odbicie w regułach, jakie aplikacje muszą spełniać przy korzystaniu w kodzie z wątków Java.

Rodzime rozsyłanie zdarzeń

Pod warstwą każdej aplikacji z graficznym interfejsem użytkownika, bez względu na język i pakiet narzędzi, w których aplikacja powstała, platforma systemu operacyjnego wykrywa zdarzenia dotyczące interfejsu graficznego i umieszcza je w kolejkach zdarzeń aplikacji. Jakkolwiek na różnych platformach szczegóły mechanizmów mogą się nieco różnić, zasada zawsze pozostaje identyczna. Gdy użytkownik kliknie przyciskiem myszy, wpisze znaki lub przejdzie do innego okna, system operacyjny generuje zdarzenia interfejsu aplikacji, takie jak kliknięcia myszą, naciśnięcia klawisza lub malowania treści okna. System ustala, które okno i która aplikacja powinny otrzymać poszczególne zdarzenia, po czym umieszcza je w kolejce zdarzeń aplikacji.

Leżąca u podłoża tych procesów struktura w każdym okienkowym interfejsie graficznym to pętla zdarzeń. Aplikacja bezpośrednio po zainicjowaniu pracy uruchamia pętlę zdarzeń, która po prostu odczytuje zdarzenia interfejsu z kolejki i odpowiada zaprogramowanymi działaniami. Działania wykonywane jako odpowiedź na zdarzenia muszą następować bardzo szybko, aby użytkownik miał wrażenie dynamicznej reakcji interfejsu na wykonywane czynności.

Długie operacje inicjowane zdarzeniami z interfejsu powinny być wykonywane w odrębnym wątku, aby wątek pętli zdarzeń mógł szybko powrócić do stanu gotowości i pobrać następne zdarzenie z kolejki. Jednak dostęp do widgetów i interfejsu API platformy ze strony innych wątków musi być kontrolowany przy użyciu jawnego blokowania i szeregowania. Aplikacja, która nie przestrzega tych reguł, może spowodować niepowodzenie odwołania do systemu operacyjnego lub wręcz zablokowanie całego graficznego interfejsu użytkownika.

Wątek interfejsu użytkownika w pakiecie SWT

Pakiet SWT działa bezpośrednio według modeli obsługi wątków udostępnianych przez platformy. Program aplikacji uruchamia pętlę zdarzeń w ramach głównego wątku i rozsyła zdarzenia bezpośrednio z tego wątku. Obiekt Display został utworzony w wątku interfejsu użytkownika. Wszystkie inne widgety muszą być również tworzone w wątku interfejsu użytkownika.

Ponieważ cały kod obsługi zdarzeń jest wyzwalany z poziomu wątku interfejsu użytkownika aplikacji, kod obsługi zdarzeń ma swobodny dostęp do widgetów i może wykonywać odwołania do funkcji graficznych bez stosowania specjalnych technik. Jednak to aplikacja jest odpowiedzialna za rozwidlanie wątków obliczeniowych przy wykonywaniu długotrwałych operacji w odpowiedzi na zdarzenie.

Uwaga: Pakiet SWT wygeneruje wyjątek SWTException dla każdego wywołania, które powinno pochodzić z wątku interfejsu użytkownika, a pochodzi z innego wątku.

Główny wątek aplikacji SWT, wraz z wątkiem pętli zdarzenia, ma następującą strukturę:

   public static void main (String [] args) {
      Display display = new Display ();
      Shell shell = new Shell (display);
      shell.open ();
      // Uruchomienie pętli zdarzeń. Zatrzymanie, gdy użytkownik
      // zamknie okno.
      while (!shell.isDisposed ()) {
         if (!display.readAndDispatch ())
            display.sleep ();
      }
      display.dispose ();
   }

Po utworzeniu widgetów i otwarciu powłoki aplikacja odczytuje i rozsyła zdarzenia z kolejki systemu operacyjnego do momentu zamknięcia okna powłoki. Jeśli w kolejce nie ma żadnych zdarzeń dla danej aplikacji, obiekt display przechodzi w stan uśpienia, dając możliwość działania innym aplikacjom.

Pakiet SWT oferuje specjalne metody dostępu, pozwalające wywoływać widgety i kod grafiki z poziomu wątków działających w tle.

Wykonywanie kodu z poziomu wątku poza interfejsem użytkownika

Aplikacje wywołujące kod interfejsu użytkownika z wątku poza interfejsem użytkownika muszą tworzyć obiekt wykonywalny typu Runnable, który wywołuje kod interfejsu użytkownika. Metody syncExec(Runnable) i asyncExec(Runnable), należące do klasy Display, umożliwiają wykonywanie takich obiektów w wątku interfejsu użytkownika w pętli zdarzeń.

Opisane metody zostały zilustrowane na przykładzie poniższego fragmentu kodu:

   // Wykonywanie czasochłonnych obliczeń
   ...
   // Aktualizacja ekranu. Obliczenia nie zależą od wyniku,
   // więc użyto metody asynchronicznej.
   display.asyncExec (new Runnable () {
      public void run () {
         if (!myWindow.isDisposed())
         myWindow.redraw ();
      }
   });
   // Dalsze obliczenia
   ...

W przypadku używania metody asyncExec dobrą praktyką jest sprawdzenie, czy widget został zutylizowany w obiekcie wykonywalnym. Ponieważ między wywołaniem metody asyncExec a wykonaniem obiektu wykonywalnego mogą wystąpić inne zdarzenia w wątku interfejsu użytkownika, nigdy nie ma pewności, w jakim stanie znajdują się widgety w czasie uruchamiania obiektu wykonywalnego.

Środowisko robocze a wątki

Reguły obsługi wątków są bardzo przejrzyste w przypadku tworzenia aplikacji SWT od podstaw, ponieważ programista ma wtedy pełną kontrolę nad tworzeniem pętli zdarzeń i rozwidlaniem wątków obliczeniowych.

Sytuacja staje się trochę bardziej skomplikowana, jeśli kod modułu dodatkowego jest wnoszony do środowiska roboczego. Poniżej przedstawiono reguły korzystania z klas interfejsu użytkownika platformy. W zależności od wersji mogą pojawiać się wyjątki od tych reguł.