2012-11-22 10 views
7

Wiele razy słyszałem, że model Java Swing jest błędny. Nie w pełni rozumiem, dlaczego, wiem, że problem jest związany z faktem, że można narysować na Drawable z innego wątku innego niż główny wątek interfejsu użytkownika. Wiem, że istnieją funkcje narzędziowe, takie jak SwingUtilities.invokeAndWait i SwingUtilities.invokeLater, które umożliwiają wykonanie obrazu w Runnable, który z kolei jest uruchamiany przez wątek Dispatcher zdarzenia. Sądzę, że w ten sposób zapewnimy, że malowanie zostanie wykonane synchronicznie, a to nie pozostawi buforu w niespójnym stanie.Dlaczego Swing modeluje błędnie i jak powinien być?

Moje pytanie brzmi:: jak zachowują się "dobre" zestawy narzędzi UI? Jakie rozwiązania zostały przyjęte?

+0

Chciałbym wiedzieć, dlaczego w tej sprawie ... co jest z tym nie tak? – gotch4

+4

Mieć upto. Myślę, że twoje pytanie jest w porządku. –

+2

Może mógłbyś zamieścić odniesienie do tego, gdzie to usłyszałeś? – Axel

Odpowiedz

9

Brian Goetz na Java Concurrency in Practice,

9,1 Dlaczego GUI jednowątkowy?:

... W dawnych czasach, aplikacje GUI były jednowątkowy i wydarzenia GUI były przetwarzane z „głównej pętli zdarzeń”. Nowoczesne środowiska graficzne używają modelu , który jest tylko nieznacznie inny: tworzą dedykowane zdarzenie wątek wysyłkowy (EDT) do obsługi zdarzeń GUI. Struktury jednowątkowe GUI nie są unikalne dla Javy; Qt, NextStep, MacOS Cocoa, X Windows i wiele innych są również jednowątkowe. To nie jest dla brak próbowania; Podjęto wiele prób napisania wielowątkowych frameworków GUI , ale z powodu trwałych problemów z warunkami wyścigu i impasem wszystkie one ostatecznie dotarły do ​​jednowątkowego modelu kolejki zdarzeń , w którym dedykowany wątek pobiera zdarzenia z kolejki i wywołań o . im aplikacji zdefiniowane zdarzenie koparki ...

+0

Innym pytaniem może być, dlaczego zamiast wywołaćAndWait i invokeLater, nie stosować podejścia, które obejmuje wywołania rysunków w parze metod BEGIN_DRAW i END_DRAW, aby zajmowały się współbieżnością?Widziałem to na przykład w OGL. – gotch4

+0

@ getch4 te metody faktycznie przechwytują akcje w 'Runnable' i umieszczają je w' EventQueue', dzięki czemu stają się dostępne dla 'EventDispatchThread'. Te działania są wykonywane w EDT. Kiedy używasz naiwnej demarkacji z metodami, które zasugerowałeś - wszystko, co przechodzi między nimi, jest wykonywane w bieżącym wątku (nie EDT). –

0

Swing ma wątek, który jest odpowiedzialny za umożliwienie użytkownikowi interakcji z graficzną częścią aplikacji. Jeśli wykonasz tylko szybkie zadania w odpowiedzi na zdarzenia zainicjowane przez użytkownika, aplikacja zawsze będzie reagować.

Być może problem występuje, jeśli wykonujesz długo działające zadanie, ze zdarzenia inicjowanego przez użytkownika, bez używania oddzielnego wątku do uruchomienia tego zadania - problem polega na tym, że podczas wykonywania zadania aplikacja zostanie zablokowana. Żadnych powtórzeń, użytkownik nie będzie w stanie wchodzić z nim w interakcję, będzie wyglądać, jakby aplikacja się zablokowała.

Jeśli wykonujesz zadanie w oddzielnym wątku (na przykład pobierasz stronę i chcesz powiadomić użytkownika, że ​​pobieranie zostało zakończone), nie możesz zaktualizować Swing bezpośrednio z tego zadania, zamiast tego musisz użyć jednej z metod pomocniczych wymienionych w twoim pytaniu.

Jest to bardziej pracochłonny proces tworzenia tych zadań, dzięki czemu aplikacja zawsze reaguje na polecenia - ale jeśli używasz czegoś, co zajmuje dużo czasu (pobranie pliku jest dobrym przykładem), aplikacja będzie nadal działać. Bądź responsywny nawet podczas wykonywania zadania, a nawet możesz pozwolić użytkownikowi anulować zadanie, tak długo, jak pozwala na to samo zadanie. Możesz użyć modalnego okna dialogowego, aby uniemożliwić użytkownikowi robienie czegoś innego podczas wykonywania zadania (jeśli chcesz to zrobić) lub mieć okno dialogowe postępu wyświetlające obracające się kółko lub coś w tym stylu. Ale myślę, że ważne jest, aby użytkownik nie pomyślał, że aplikacja po prostu "zamarła" bez powodu.

+0

Dziękuję za odpowiedź, ale to jest część, którą znałem mniej więcej :) Chciałbym wiedzieć, jak problem został rozwiązany w innych systemach interfejsu użytkownika – gotch4

+0

@ gotch4 Oni nie radzą sobie z tym inaczej. Działają tak bardzo jak oni wszyscy. – nos

1

Sposób implementacji obecnych technologii wyświetlania, malowanie pikseli na ekranie jest zawsze szeregowe. Musisz wygenerować około 30 obrazów na sekundę i malować je jeden po drugim.

Nie ma potrzeby, aby ten obraz był wielowątkowy, ponieważ nadal musiałbyś wykonać synchronizację w tle. I to właśnie robi Swing - wykorzystuje specjalny wątek o nazwie Event Dispatch Thread, aby zaplanować wszystkie zmiany, które nastąpią w czasie przed następnym obrazem.

Technicznie, Swing jest bezpieczny dla wątków, JEŚLI używasz EDT do przesyłania zmian. I do tego właśnie służą metody. Przesyłają zmiany do EDT.

Jeśli nie używasz EDT i nie przesyłasz długookresowych zmian, takich jak obliczenie pewnej wartości po naciśnięciu przycisku, możesz zobaczyć, że aplikacja przestała odpowiadać, a nie malować się. Ponieważ EDT jest zajęty wykonywaniem obliczeń dla ciebie i nie ma czasu na planowanie powtórek i innych zdarzeń.

3

dla SWT: http://book.javanb.com/swt-the-standard-widget-toolkit/ch05lev1sec7.html

SWT implementuje jednowątkowego modelu interfejsu użytkownika, który jest zwykle o nazwie apartament gwintowania. W tym modelu tylko wątek interfejsu użytkownika może wywoływać operacje interfejsu użytkownika. Ta zasada jest ściśle egzekwowana. Jeśli spróbujesz uzyskać dostęp do obiektu SWT spoza wątku interfejsu użytkownika, otrzymasz wyjątek SWTException ("Nieprawidłowy dostęp do wątku").

Zatem SWT jest również jednowątkowy. Ale robi dodatkowy krok, aby zabronić jakichkolwiek zmian w interfejsie użytkownika poza wątkiem interfejsu użytkownika. Rozważ alternatywę w Swingu, gdzie modyfikowanie interfejsu z innego miejsca jest dozwolone, ale spowoduje wcześniejsze lub późniejsze nieoczekiwane wyniki, które zmyli programistę newbie, który następnie dowie się, że Swing jest jednotrenowym "twardym" sposobem.

Ponadto, jeśli twój projekt nie jest jasny, możesz skończyć z sytuacjami, w których myślisz, że jesteś we właściwym wątku, ale w rzeczywistości tak nie jest. Możesz również nie być w stanie określić, które wątki będą miały dostęp do określonego fragmentu kodu, ale prawdopodobnie masz poważny problem z projektowaniem w swoim własnym kodzie.

Poza tym, nie wyobrażam sobie innych powodów, dla których model gwintowania Swinga byłby uważany za "niewłaściwy".

+0

W jaki sposób SWT może wykryć, że inny wątek zakłóca działanie interfejsu użytkownika? Czy wszystkie metody zestawów narzędzi są podawane do serwerów proxy, a kontrola jest wykonywana? – gotch4

+0

Nie jestem pewien, ponieważ nie jestem bardzo doświadczony w SWT, ale najwyraźniej nie, ponieważ ślad stosu błędu nie pokazuje proxy i pokazuje, że wyjątek pochodzi od metod, których nazwy wskazują, że sprawdzają coś, np. Org. eclipse.swt.widgets.Display.checkDevice. W rzeczywistości nie wszystkie metody rzucają wyjątek. Ci, którzy chcą, są udokumentowani jako podatni na odrzucenie wyjątku SWTEx z wartością ERROR_THREAD_INVALID_ACCESS. –

+0

Może to sprawdzić w taki sam sposób jak robi to JavaFX lub jak robi Swing, jeśli używasz niestandardowego RepaintManager, jak opisano w [tym artykule] (http://weblogs.java.net/blog/alexfromsun/archive/2006/02/ debugging_swing.html) – Robin

Powiązane problemy