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_t mutex; |
2 | pthread_cond_t count_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 |
12 | pthread_cond_signal(&count_cv); // comsumer signalisieren |
13 | }
|
14 | ...
|
15 | }
|
16 | |
17 | void *worker_thread_2(void *args) { |
18 | ...
|
19 | pthread_mutex_lock(&mutex); |
20 | insert_item(fifo, cmd2); |
21 | number_items = num_item(fifo); |
22 | pthread_mutex_unlock(&mutex); |
23 | if (number_items==1) { //übergang von leerem fifo zu einem Eintrag |
24 | pthread_cond_signal(&count_cv); // comsumer signalisieren |
25 | }
|
26 | ...
|
27 | }
|
28 | |
29 | |
30 | void *winproc_thread(void *args) { |
31 | for (;;) { |
32 | pthread_mutex_lock(&mutex); |
33 | if (num_item(fifo)==0) {// keine Befehle im FIFO, bitte warten |
34 | pthread_cond_wait(&count_cv, &mutex); |
35 | }
|
36 | remove_item(fifo, &cmd); // get update-command |
37 | pthread_mutex_unlock(&mutex); |
38 | |
39 | gdk_threads_enter(); |
40 | |
41 | gtk_update( ...), // je nach command cmd |
42 | |
43 | gdk_threads_leave(); |
44 | }
|
45 | }
|
46 | |
47 | |
48 | int main(int argc, char *argv[]) { |
49 | |
50 | ...
|
51 | |
52 | if (!g_thread_create(winproc_thread, &ctx1, FALSE, &error)) { |
53 | g_printerr ("Failed to create YES thread: %s\n", error->message); |
54 | return 1; |
55 | }
|
56 | |
57 | gdk_threads_enter(); |
58 | gtk_main(); |
59 | gdk_threads_leave(); |
60 | |
61 | ...
|
62 | }
|
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
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.