2013-09-05 19 views
9

Sto creando un semplice C application utilizzando GTK, ma devo eseguire alcuni IO di blocco che attiveranno gli aggiornamenti alla GUI. Per fare questo, comincio una nuova pthread destra prima gtk_main() in quanto tale:Problemi di filettatura con GTK

/* 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(); 

Quando il pthread legge alcuni dati, si aggiorna l'interfaccia grafica in questo modo:

g_main_context_invoke(mainc, set_icon, param); 

Dove set_icon è

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

Questo tutto funziona la maggior parte del tempo, ma ogni tanto ottengo questo curioso messaggio di errore:

 
[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. 

Ho pensato che il punto principale dell'utilizzo di g_main_context_invoke era evitare problemi con i thread? Facendo un po 'di googling, mi sono imbattuto in gdk_threads_init, gdk_threads_enter e amici, ma sembrano tutti deprecati? So che la documentazione di GTK dice che tutti gli aggiornamenti della GUI dovrebbero essere eseguiti sul thread principale, ma questo non combina molto bene con l'IO di blocco, e preferirei non dover costruire un meccanismo di comunicazione complesso tra i thread.

E quindi, la mia domanda è, come devo affrontare correttamente questo?

EDIT: Il codice completo è visibile here EDIT2: come un aggiornamento in base alla risposta di @ ptomato, mi sono trasferito a GThread s ed utilizzando gdk_threads_add_idle() come si vede nella this impegnarsi, ma il problema è ancora presente.

+0

Avete un po 'di codice disponibile, sto usando gtk + per un bel po' di tempo e non è incappato in questo problema .. – drahnr

+0

L'intero codice è disponibile su [GitHub] (https://github.com/jonhoo/mktrayicon) come collegato al post. –

+0

Ah, mancato grazie! – drahnr

risposta

8

chiamata XInitThreads(). Questo dovrebbe essere fatto prima del gtk_init, che fermerà i messaggi!

Qualcosa di simile a questo:

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

Non mi ricordo di aver visto questi messaggi prima GLIB 2.32, quando g_thread_init()/gdk_threads_init() sono stati utilizzati.

Si potrebbe voler verificare g_thread_pool_new e g_thread_pool_push. Da filo, utilizzare g_main_context_invoke da eseguire nel ciclo principale o semplicemente avvolgere filo tra gdk_threads_enter()/gdk_threads_leave()

non uso un vassoio quindi non posso controllata agevolmente.Penso che tu sia corretto su gdk_threads_add_idle utilizzando i blocchi per proteggere le API GTK/GDK. Non c'è nulla di ovvio per me che faccia apparire questi messaggi a . La descrizione della funzione per gtk_status_icon_new_from_icon_name afferma che "Se il tema dell'icona corrente viene modificato, l'icona sarà aggiornata a . Che per me, implica che il codice non è l'unico codice che accederà alla visualizzazione X, che potrebbe essere il problema.

ci sono anche alcune informazioni correlate per quanto riguarda XInitThreads() in

What is the downside of XInitThreads()?

Nota che mentre GDK utilizza i blocchi per il display, GTK/GDK Non bisogna mai XInitThreads chiamata.

Su un lato nota: Cosa sta proteggendo la variabile globale "onclick", che viene passato a execl dopo una fork(), Il bambino non erediterà blocca la memoria del padre, e GLib mainloop è incompatibile con fork() . Forse potresti copiare la stringa sulla variabile locale.

+0

'gdk_threads_enter' e' gdk_threads_leave' sono stati deprecati, quindi usarli non è davvero un'opzione. Considerando che ho solo un thread aggiuntivo, usare un pool di thread sembra un po 'eccessivo, ma grazie per il suggerimento! Sembra strano dover chiamare 'XInitThreads' manualmente, considerando che il manuale dice" Il sistema di threading di GLib viene inizializzato automaticamente all'inizio del programma ", ma ci penserò domani e riferirò. –

+1

Inoltre, non è un po 'strano dover chiamare 'XInitThreads' quando la documentazione di' gdk_threads_add_idle_full' dice che "chiama' function' con il blocco GDK in attesa ", e i documenti' XInitThreads' dicono "Se tutte le chiamate alle funzioni di Xlib sono protette da qualche altro meccanismo di accesso (ad esempio un blocco di esclusione reciproca in un toolkit o tramite programmazione client esplicita), l'inizializzazione del thread di Xlib non è richiesta "? –

+0

[Questo] (https://github.com/Jonhoo/mktrayicon/commit/77a869fd4612c0cdffdee2f19ae5db3dda77adb6) ha risolto il problema, grazie! Vorrei ancora qualche informazione in più su ** perché ** questo sta accadendo per la taglia. –

1

Non sono sicuro che i pthread nudi siano garantiti per funzionare con GTK. Dovresti usare i wrapper GThread.

Penso che il problema potrebbe essere che g_main_context_invoke() aggiunge set_icon() come una funzione inattiva. (Sembra che sia quello che succede dietro le quinte, ma non ne sono sicuro.) Le funzioni di inattività aggiunte usando l'API di GLib, nonostante siano eseguite sul thread principale, devono contenere il blocco GDK. Se si utilizza l'API gdk_threads_add_idle() (che non è deprecata) per richiamare set_icon(), allora tutto dovrebbe funzionare correttamente con il threading.

(Anche se questa è solo una supposizione.)

+0

Ho cambiato in GThreads e 'gdk_threads_add_idle' come visto [in questo commit] (https://github.com/Jonhoo/mktrayicon/commit/e62ede1cf0e863e0e9173d15f46a01bd6536fa11), ma sto ancora vedendo l'errore (o piccole variazioni di esso) occasionalmente ... –

+0

Sì, GTK e PThreada sono compatibili. – Lothar

0

Come soluzione, se si desidera evitare di bloccare l'interfaccia utente mentre si attende un qualche IO, è possibile utilizzare l'I/O asincrono da GIO. Ciò eviterebbe di dover gestire autonomamente i thread.

Modifica: Pensandoci potresti semplicemente contrassegnare i descrittori di file come non bloccanti e aggiungerli come sorgente al ciclo principale di glib e li interrogherà nel ciclo degli eventi principale senza dover fare confusione con i thread .

+0

Polling non sembra una soluzione particolarmente pulita. Preferisco bloccarlo perché significherebbe che il programma reagirà quasi immediatamente ai comandi. GIO sembra interessante però. Potresti dare un esempio di come potrei usarlo per questo? –

+0

Il polling non dovrebbe essere più lento di una lettura bloccata, è ciò che GTK usa per reagire agli eventi del mouse e della tastiera. Per fare questo crea un 'GIOChannel' con' g_io_channel_new_file' e aggiungilo al ciclo principale con 'g_io_add_watch'. Per GIO creare un 'GFile' con' g_file_new_for_commandline_arg' o 'g_file_new_for_path' quindi chiamare' g_file_read_async' con un callback che chiama 'g_file_read_finish' seguito da' g_input_stream_read_async' con un callback che chiama 'g_input_stream_read_finish' e quindi elabora i dati letti e poi chiama 'g_input_stream_read_async' di nuovo. –

0

È possibile evitare l'uso dei thread utilizzando gio_add_watch() che invocherà la funzione di richiamata quando sono disponibili dati sul canale.

+0

Non del tutto banali con i file FIFO, perché nel modo in cui li sto usando, dovranno essere riaperti (quali blocchi) ogni volta che si incontra EOF mentre gli scrittori vanno e vengono. 'g_io_add_watch' non sembra supportare questo? Correggimi se sbaglio. –