GUIs und Threads
« | 08 Dec 2018 | »Auch wenn modernere Frameworks etwas anderes versprechen, so haben alle bekannten GUI Umgebungen die Eigenschaft, dass sie im Kern “single-threaded” aufgebaut sind.
Oft kommt der Einwand: “Das kommt aus der Vergangenheit”, aber das hätte man schon lange ändern können.
In Wahrheit hat es einige Vorteile, wenn UI Aktionen linear abgearbeitet werden können.
Alle UIs nutzen intern eine Form von Message-Queue. Jede Interaktion wird als Eintrag hinten angefügt, während am anderen Ende ein Worker diese aufgreift und abarbeitet.
Wir befinden uns hier in einer
Event-driven Architecture.
Alles geht von der Benutzerinteraktion aus.
Das funktioniert in einem linearen (sequentiellen) System sehr geht, weil sich
durch die Queue eine feste Reihenfolge ergibt - und zwar eine
FIFO.
Ein späteres Ereignis kann kein früheres überholen.
Aus diesem Grund wollen UI Kernkomponenten, dass alle Aufrufe ihrer Funktionen nur aus einem einzigen Thread heraus erfolgen. Und das ist dann der sagenumwobene UI Thread.
Der größte Unterschied zwischen der
Windows API
und der X-lib von
X11 ist, dass X11 als
Netzwerkprotokoll ziemlich asynchron aufgebaut ist.
Während man in der Windows API eine Funktion aufruft und nach deren
Rückkehr weiß, dass die Aktion umgesetzt ist, muss das bei X11 noch
nicht so sein.
Führt man also als Reaktion zu Event A einen Statuswechsel aus, so weiß man, dass unter Windows im nachfolgenden Event B der neue Status schön gültig ist, während man bei X11 nicht wissen kann, ob der Statuswechsel schon am X-Server abgearbeitet wurde.
Viele GUI Bibliotheken müssen daher in ihren eigenen internen Aufzeichnungen darüber Buch führen, welche Zustände schon bestätigt sind und welche noch in der Luft hängen und im Zweifelsfall Aktionen verschieben, bis eine Form der Bestätigung ankommt.
Den gleichen Overhead betreiben moderne UI Frameworks, die sich das Wort asynchron auf die Brust schreiben. Sie müssen sicherstellen, dass Zustände nicht von parallelen Aufrufen korrumpiert werden.
Für mich ist dabei ärgerlich, warum sie das still und heimlich tun. Der Grund ist, dass Anwendungsprogrammierer mit dem Thema Threading oft überfordert sind und sich einfach nicht damit befassen wollen (und manche es auch einfach nicht können).
In den meisten Fällen geht es um das Übertragen einer Meldung aus einem Workerthread in den UI-Thread, an dem es scheitert. Also übernehmen das die Frameworks mit generischen Ansätzen. Das ist zwar grundsätzlich nicht schlecht … aber besonders gut ist das auch nicht, denn der generische Overhead ließe sich mit ein paar Zeilen Eigeninitiative individuell oft viel effizienter bzw. besser lösen.
Ein weiterer Nachteil ist, dass durch das fehlende Verständnis der Anwendungsprogrammierer letztendlich dennoch Statusverletzungen als Ergebnis auftreten können.
… und dann stürzt eben die Anzeigetafel der Zugverbindungen am Bahnhof ab, wenn tatsächlich zwei Züge gleichzeitig einfahren (auf unterschiedlichen Gleisen natürlich!), weil auf die Synchronisierung der eigenen Variablen vergessen wurde.
… so etwas habe ich leider schon viel zu oft sehen müssen.
UIs sind ein spannendes Thema, dessen Aufbau im Detail leider oft wegen einem simplifizierenden Business-Blabla in den Hintergrund tritt.
Dagegen müssen wir was unternehmen! ;)