Threading-Probleme mit GTK

Lesezeit: 7 Minuten

Benutzer-Avatar
Jon Gjengset

Ich baue eine ziemlich einfache C-Anwendung Verwenden von GTK, müssen jedoch einige blockierende E / A ausführen, die Aktualisierungen der GUI auslösen. Dazu fange ich neu an pthread kurz bevor gtk_main() als solche:

/* global variables */
GMainContext *mainc;

/* local variables */
FILE *fifo;
pthread_t reader;

/* main() */
mainc = g_main_context_default();
pthread_create(&reader, NULL, watch_fifo, argv[argc-1]);
gtk_main();

Wenn der pthread liest einige Daten, aktualisiert es die GUI wie folgt:

g_main_context_invoke(mainc, set_icon, param);

Wo set_icon ist

gboolean set_icon(gpointer data)
{
    char *p = (char*)data;
    gtk_status_icon_set_from_icon_name(icon, p);
    return FALSE;
}

Meistens funktioniert das alles, aber hin und wieder bekomme ich diese merkwürdige Fehlermeldung:

[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
mktrayicon: xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.

Ich dachte, der ganze Sinn der Verwendung g_main_context_invoke war es, Probleme mit Threads zu vermeiden? Beim Googeln bin ich darauf gestoßen gdk_threads_init, gdk_threads_enter und Freunde, aber sie scheinen alle veraltet zu sein? Ich weiß, dass die GTK-Dokumentation besagt, dass alle GUI-Updates im Haupt-Thread durchgeführt werden sollten, aber dies lässt sich nicht so gut mit dem Blockieren von IO kombinieren, und ich würde es vorziehen, keinen komplexen Kommunikationsmechanismus zwischen den Threads aufbauen zu müssen.

Nun meine Frage, wie gehe ich richtig damit um?

EDIT: Der vollständige Code ist zu sehen hier
EDIT2: Als Update basierend auf der Antwort von @ptomato bin ich umgezogen GThreads und verwenden gdk_threads_add_idle() wie in … gesehen Dies committen, aber das Problem ist immer noch vorhanden.

  • Haben Sie Code zur Verfügung, ich benutze gtk+ seit geraumer Zeit und noch nie bin über dieses problem gestolpert..

    – drahnr

    10. September 2013 um 17:43 Uhr

  • Den gesamten Code finden Sie unter GitHub wie im Beitrag verlinkt.

    – Jon Gjengset

    10. September 2013 um 18:09 Uhr

  • Ah, das verpasst danke!

    – drahnr

    10. September 2013 um 18:28 Uhr

  • Beachten Sie, dass Sie verwenden möchten Dies Link, um den Originalcode zu sehen, und Dies Link, um den Code nach der Umsetzung der Vorschläge von @ptomato zu sehen. Dies Link verweist Sie auf die korrigierte Version der Datei.

    – Jon Gjengset

    10. September 2013 um 18:35 Uhr

Benutzer-Avatar
Wiley

Anruf XInitThreads(). Dies sollte vorher erfolgen gtk_initdas wird die Nachrichten stoppen!

Etwas wie das:

    #include <X11/Xlib.h>
    ...  
    XInitThreads();
    ...
    gtk_init(&argc, &argv);

Ich kann mich nicht erinnern, diese Nachrichten vor GLIB 2.32 gesehen zu haben, als
g_thread_init()/gdk_threads_init() wurden verwendet.

Vielleicht möchten Sie auschecken g_thread_pool_new und g_thread_pool_push. Aus Thread verwenden g_main_context_invoke in der Hauptschleife auszuführen oder den Thread einfach dazwischen zu wickeln gdk_threads_enter()/gdk_threads_leave()

Ich verwende kein Tablett, daher kann ich dies nicht einfach überprüfen. Ich denke, Sie haben Recht mit gdk_threads_add_idle, das Sperren zum Schutz der GTK/GDK-API verwendet. Für mich ist nichts offensichtlich, was dazu führen würde, dass diese Nachrichten erscheinen. In der Funktionsbeschreibung für gtk_status_icon_new_from_icon_name heißt es: „Wenn das aktuelle Symbolthema geändert wird, wird das Symbol entsprechend aktualisiert. Was für mich bedeutet, dass Ihr Code nicht der einzige Code ist, der auf die X-Anzeige zugreift, was möglicherweise das Problem sein könnte.

Es gibt auch einige verwandte Informationen zu XInitThreads() unter

Was ist der Nachteil von XInitThreads()?

Beachten Sie, dass während GDK Sperren für die Anzeige verwendet, GTK/GDK niemals XInitThreads aufruft.

Nebenbei bemerkt: Was schützt die globale Variable “onclick”, die nach einem fork() an execl übergeben wird, das untergeordnete Element erbt nicht die Speichersperren des übergeordneten Elements, und GLib mainloop ist mit fork() nicht kompatibel. Vielleicht könnten Sie die Zeichenfolge in eine lokale Variable kopieren.

  • gdk_threads_enter und gdk_threads_leave sind veraltet, daher ist ihre Verwendung nicht wirklich eine Option. Wenn man bedenkt, dass ich nur einen zusätzlichen Thread habe, erscheint die Verwendung eines Thread-Pools etwas übertrieben, aber danke für den Tipp! Scheint seltsam, anrufen zu müssen XInitThreads manuell unter Berücksichtigung des Handbuchs heißt es “Das GLib-Threading-System wird beim Start Ihres Programms automatisch initialisiert”, aber ich werde es später heute versuchen und berichten.

    – Jon Gjengset

    9. September 2013 um 10:07 Uhr

  • Außerdem ist es nicht ein bisschen seltsam, anrufen zu müssen XInitThreads wenn die Dokumentation für gdk_threads_add_idle_full sagt, dass es “anruft function bei gehaltener GDK-Sperre”, und die XInitThreads docs sagen: “Wenn alle Aufrufe von Xlib-Funktionen durch einen anderen Zugriffsmechanismus geschützt sind (z. B. eine gegenseitige Ausschlusssperre in einem Toolkit oder durch explizite Client-Programmierung), ist keine Xlib-Thread-Initialisierung erforderlich”?

    – Jon Gjengset

    9. September 2013 um 10:13 Uhr

  • Dies Problem behoben, danke! Hätte gerne noch ein paar Infos zu warum Dies geschieht für das Kopfgeld.

    – Jon Gjengset

    9. September 2013 um 17:16 Uhr

  • Nachdem Sie den Link in Ihrer Antwort gelesen haben XInitThreads, Ich mag es nicht, es anrufen zu müssen, obwohl es funktioniert. Es scheint, als ob mit gdk_threads_add_idledie bereits die GTK-Sperre erhält, sollte ausreichen … In Bezug auf Ihre Randbemerkung sollte dies kein Problem sein, da der Speicherplatz von kopiert wird fork und dann “gewischt” durch execl. Die Eltern onclick var wird freigegeben, wenn sie weiter unten geändert wird. Wenn Sie an X-Sperren denken, die neu forked-Prozess wird völlig unabhängig sein und sollte sich nicht darum kümmern müssen, dass GDK/GTK ihn überhaupt startet.

    – Jon Gjengset

    11. September 2013 um 0:19 Uhr


  • Ich denke, dies kann ein Fehler in GTK oder GDK sein. Wenn Sie es auf ein Minimalbeispiel reduzieren können (zugegebenermaßen schwer mit Threading-Bugs zu tun), verdient es definitiv einen Bericht auf bugzilla.gnome.org.

    – Tomaten

    11. September 2013 um 6:42 Uhr

Ich bin mir nicht sicher, ob Bare-Pthreads garantiert mit GTK funktionieren. Sie sollten die GThread-Wrapper verwenden.

Ich denke, was das Problem sein könnte, ist das g_main_context_invoke() fügt hinzu set_icon() als Leerlauffunktion. (Es scheint, dass das hinter den Kulissen vor sich geht, aber ich bin mir nicht sicher.) Idle-Funktionen, die mit der API von GLib hinzugefügt werden, müssen, obwohl sie im Haupt-Thread ausgeführt werden, die GDK-Sperre halten. Wenn Sie die verwenden gdk_threads_add_idle() API (die nicht veraltet ist) zum Aufrufen set_icon()dann sollte mit dem Threading alles richtig funktionieren.

(Obwohl dies nur eine wilde Vermutung ist.)

  • Ich habe zu GThreads gewechselt und gdk_threads_add_idle wie gesehen in diesem Commitaber ich sehe den Fehler (oder geringfügige Variationen davon) gelegentlich immer noch …

    – Jon Gjengset

    6. September 2013 um 9:56 Uhr

Benutzer-Avatar
Philipp Holz

Um dies zu umgehen, wenn Sie nur vermeiden möchten, die Benutzeroberfläche zu blockieren, während Sie auf einige IO warten, können Sie das asynchrone IO von verwenden GIO. Das würde verhindern, dass Sie Threads selbst verwalten müssen.

Bearbeiten: Wenn Sie darüber nachdenken, könnten Sie Ihre Dateideskriptoren einfach als nicht blockierend markieren und sie als Quelle zur Glib-Hauptschleife hinzufügen, und sie werden sie in der Hauptereignisschleife für Sie abfragen, ohne sich mit Threads herumschlagen zu müssen.

  • Polling scheint jedoch keine besonders saubere Lösung zu sein. Ich würde es lieber blockieren, da dies bedeuten würde, dass das Programm ziemlich sofort auf Befehle reagiert. GIO scheint aber interessant zu sein. Können Sie ein Beispiel geben, wie ich es dafür verwenden könnte?

    – Jon Gjengset

    9. September 2013 um 19:35 Uhr

  • Polling sollte nicht langsamer sein als ein blockierendes Lesen, es ist das, was GTK verwendet, um auf Maus- und Tastaturereignisse zu reagieren. Erstellen Sie dazu eine GIOChannel mit g_io_channel_new_file und füge es mit zur Hauptschleife hinzu g_io_add_watch. Erstellen Sie für GIO a GFile mit g_file_new_for_commandline_arg oder g_file_new_for_path Dann ruf an g_file_read_async mit einem Rückruf, der anruft g_file_read_finish gefolgt von g_input_stream_read_async mit einem Rückruf, der anruft g_input_stream_read_finish und verarbeitet dann die gelesenen Daten und ruft dann auf g_input_stream_read_async wieder.

    – Philipp Holz

    10. September 2013 um 13:15 Uhr

Sie könnten die Verwendung von Threads vermeiden, indem Sie gio_add_watch() verwenden, das Ihre Callback-Funktion aufruft, wenn Daten auf dem Kanal verfügbar sind.

1159270cookie-checkThreading-Probleme mit GTK

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy