Wenn man mit z.B. MS-Windows-GUI programmiert, und man hat einen
Workerthread oder eine Callback-Funktion, dann kann diese der Win-Proc
eine Botschaft (z.B. WM_USER) posten, die dann z.B. ein paar Updates
bewirkt (z.B. Text in einem Textfeld).
Auch bei Java/Swing gibt es einfache und klare Strukturen um
Window-Updates vom Worker-Thread zum EDT (Event Dispatch Thread) zu
delegieren.
(GUI-Modifikation sollen nur von einem einzigen Thread ausgeführt
werden)
Nur bei GTK komme ich mit diesem (Standard-) Problem nicht weiter.
Der UI-Thread verschwindet in gtk_main() und aus ist es.
Wenn man nach "multithreaded GTK" googelt, kommt man gleich auf die
g_threads_init() - gdk_threads_init() gdk_threads_enter() -
gdk_threads_leave() Methode, z.B.
http://blogs.operationaldynamics.com/andrew/software/gnome-desktop/gtk-thread-awareness
Aber das macht doch den Code kompliziert und bremst den Workerthread
aus?!
Was wäre eine vernünftige Methode, um z.B. von einem
Raw-Midi-In-Callback einen Counter (statisches/nicht-editierbares
Textfeld) auf dem Hauptwindow zu erhöhen?
gtk_label_set_label() geht vermutlich nicht, da das vom Callback-Thread
(synchron) ausgeführt würde. Man bräuchte so ein
"gtk_Post_label_set_label()", was den Textfeld-Update asynchron vom
gtk_main()-Thread ausführen lässt.
Ich habe mir so eine Struktur überlegt, die ganz gut funktioniert.
Die worker-threads schreiben update-Befehle in ein Fifo.
Parallel zum gtk_main() läuft noch ein anderer UI-Thread
"winproc_thread",
der das Messagefifo "fifo" liesst und thread-safe die entsprechenden
Updates macht.
1
pthread_mutex_tmutex;
2
pthread_cond_tcount_cv;
3
"fifo"
4
5
void*worker_thread_1(void*args){
6
...
7
pthread_mutex_lock(&mutex);
8
insert_item(fifo,cmd1);
9
number_items=num_item(fifo);
10
pthread_mutex_unlock(&mutex);
11
if(number_items==1){//übergang von leerem fifo zu einem Eintrag
guint g_idle_add (GSourceFunc function,
gpointer data);
Adds a function to be called whenever there are no higher priority
events pending to the default main loop. The function is given the
default idle priority, G_PRIORITY_DEFAULT_IDLE. If the function returns
FALSE it is automatically removed from the list of event sources and
will not be called again.
mfg Andreas
Danke, das scheint eine gute Idee zu sein.
So eine "idle" function kann das Message-Fifo abarbeiten, und da sie vom
main-thread aufgerufen wird, ist sie von vornherein "thread-safe" ohne
die entsprechenden Funktionen (gdk_threads_enter() ...).
Jürgen G. schrieb:> Danke, das scheint eine gute Idee zu sein.> So eine "idle" function kann das Message-Fifo abarbeiten, und da sie vom> main-thread aufgerufen wird, ist sie von vornherein "thread-safe" ohne> die entsprechenden Funktionen (gdk_threads_enter() ...).
Doch, du musst Synchronisieren. Ich habe mir schon so den XServer
gekillt, hat der garnicht gern...
[Edit] Auch Windows hat das garnicht gern...
http://developer.gnome.org/gdk/stable/gdk-Threads.html
Idles, timeouts, and input functions from GLib, such as g_idle_add(),
are executed outside of the main GTK+ lock. So, if you need to call GTK+
inside of such a callback, you must surround the callback with a
gdk_threads_enter()/gdk_threads_leave() pair or use
gdk_threads_add_idle_full() which does this for you. However, event
dispatching from the mainloop is still executed within the main GTK+
lock, so callback functions connected to event signals like
GtkWidget::button-press-event, do not need thread protection.
mfg Andreas
Ein weiteren Nachteil hat dieser Ansatz dennoch:
Ich kann die idle-function natürlich nicht mit
1
pthread_cond_wait(&count_cv,&mutex)
schlafenlegen und bei Ankunft einer Message aufwecken.
Die idle-function muss immer erst prüfen ob im Fifo was drin ist und
sofort zurückkehren. Das kann in einer busy-loop ausarten, je nachdem
wie oft sie vom main-thread aufgerufen wird.
Mal ausprobieren ...
Jürgen G. schrieb:> Ein weiteren Nachteil hat dieser Ansatz dennoch:>> Ich kann die idle-function natürlich nicht mit>
1
>pthread_cond_wait(&count_cv,&mutex)
2
>
> schlafenlegen und bei Ankunft einer Message aufwecken.
Warum die Funktion nicht erst beim einfügen einer Message starten? Dann
werden alle Messages abgearbeitet, und danach wird die Funktion nicht
mehr aufgerufen, bis du das nächste mal etwas in die Queue einfügst.
> Die idle-function muss immer erst prüfen ob im Fifo was drin ist und> sofort zurückkehren. Das kann in einer busy-loop ausarten, je nachdem> wie oft sie vom main-thread aufgerufen wird.> Mal ausprobieren ...
Das Problem hättest du dann nicht...
mfg Andreas
Ohne böse klingen zu wollen:
Ich verstehe nicht, warum man heute noch diese rudimentären APIs und
dergleichen Ansätze verfolgt, es sei denn, es gibt ein rein akademisches
Interesse oder ein hinreichend persönliches Problem, um seine Defizite
mit "Spezial Wissen" (was keiner braucht) auszugleichen.
Es gibt doch mittlerweile eine Vielzahl an Alternativen, je nach Gusto.
Wenn man das Rad neu beschreibt, wird es wahrscheinlich nicht ganz rund.
> Warum die Funktion nicht erst beim einfügen einer Message starten?
Weil dann der Worker-Thread diesen Aufruf machen müsste?
Vermutlich ist g_idle_add() anders als die gtk-UI Funktionen, aber im
Worker-Thread wollte ich diese Funktionen vermeiden. Wenn ich z.B. ein
Peak-Can-Interface mit 500kBaud auslese, dann darf er einfach nicht auf
unbekannt blockiert werden.
>Autor: .NET (Gast)>Datum: 23.07.2012 21:38>>Ohne böse klingen zu wollen:>>Ich verstehe nicht, warum man heute noch diese rudimentären APIs
Ich denke, dass GTK eine sehr hochstehende, bequeme und umfangreiche API
bietet. Verglichen mit Java/Swing/Awt sehe ich von der Komplexität keine
großen Unterschiede. In null komma nix hat man Menus, Controls (Buttons
...) eingebunden.
Xlib wäre umständlicher zu programmieren.
Jürgen G. schrieb:>> Warum die Funktion nicht erst beim einfügen einer Message starten?>> Weil dann der Worker-Thread diesen Aufruf machen müsste?
Ja
> Vermutlich ist g_idle_add() anders als die gtk-UI Funktionen, aber im> Worker-Thread wollte ich diese Funktionen vermeiden. Wenn ich z.B. ein> Peak-Can-Interface mit 500kBaud auslese, dann darf er einfach nicht auf> unbekannt blockiert werden.
Ja gut, g_idle_add() ist synchronisiert, kann daher blockieren. Wenn du
es selbst machst kannst du ggf. sogar eine Atomic Linked List bauen.
Mussten wir mal in einem Praktikum...
Jürgen G. schrieb:>>Autor: .NET (Gast)>>Datum: 23.07.2012 21:38>>>>Ohne böse klingen zu wollen:>>>>Ich verstehe nicht, warum man heute noch diese rudimentären APIs>> Ich denke, dass GTK eine sehr hochstehende, bequeme und umfangreiche API> bietet. Verglichen mit Java/Swing/Awt sehe ich von der Komplexität keine> großen Unterschiede. In null komma nix hat man Menus, Controls (Buttons> ...) eingebunden.> Xlib wäre umständlicher zu programmieren.
GTK ist lediglich mit der Sprache C dem Konfort von Java unterlegen.
Von der GUI Lib her ist GTK sehr schön aufgebaut. Zudem braucht es keine
kostenpflichtige Software und ist auch nicht Closed Source wie z.B.
.NET.
mfg Andreas