2013-09-05 17 views
9

Buduję dość prosty C application używając GTK, ale muszę wykonać blokujące IO, które wyzwoli aktualizacje GUI. W tym celu, zaczynam nowy pthread prawo przed gtk_main() jako takie:Zgrywanie wątków z 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(); 

Gdy pthread czyta jakieś dane, to aktualizuje GUI tak:

g_main_context_invoke(mainc, set_icon, param); 

Gdzie set_icon jest

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

To wszystko działa przez większość czasu, ale co jakiś czas pojawia się ciekawy komunikat o błędzie:

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

Myślałem, że jedynym celem korzystania z g_main_context_invoke było unikanie problemów z wątkami? Trochę Googling, natknąłem gdk_threads_init, gdk_threads_enter i znajomych, ale wszystkie wydają się być przestarzałe? Wiem, że dokumentacja GTK mówi, że wszystkie GUI updaes powinny być wykonywane na głównym wątku, ale to nie łączy się dobrze z blokowaniem IO i wolałbym nie budować jakiegoś skomplikowanego mechanizmu komunikacji między wątkami.

Moje pytanie brzmi: w jaki sposób powinienem poradzić sobie z tym?

EDIT: Pełny kod widać here Edit2: jako aktualizacja na podstawie odpowiedzi @ ptomato za, ja przeniósł się do GThread s i korzystania gdk_threads_add_idle() jak widać w this popełnić, ale problem nadal występuje.

+0

Czy masz dostęp do kodu, używam gtk + od jakiegoś czasu i _never_ natknąłem się na ten problem .. – drahnr

+0

Cały kod jest dostępny na [GitHub] (https://github.com/jonhoo/mktrayicon) w powiązaniu z postem. –

+0

Ah, przegapiłem to dzięki! – drahnr

Odpowiedz

8

połączeń XInitThreads(). Należy to zrobić przed gtk_init, aby zatrzymać wiadomości!

coś takiego:

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

nie pamiętam widząc te wiadomości przed Glib 2.32, kiedy g_thread_init()/gdk_threads_init() były używane.

Może chcesz wypróbować g_thread_pool_new i g_thread_pool_push. z nici, użyj g_main_context_invoke do wykonania w pętli głównej lub prostu owinąć nitkę między gdk_threads_enter()/gdk_threads_leave()

nie używam tacę, więc nie mogę łatwo sprawdzić.Myślę, że jesteś poprawny o gdk_threads_add_idle używając blokad, aby chronić API GTK/GDK. Nie ma dla mnie oczywistych powodów, które spowodowałyby pojawienie się tych wiadomości do . Opis funkcji dla gtk_status_icon_new_from_icon_name stwierdza, że ​​"Jeśli aktualny motyw ikon zostanie zmieniony, ikona będzie odpowiednio zaktualizowana . Który dla mnie oznacza, że ​​twój kod nie jest jedynym kodem , który będzie miał dostęp do wyświetlacza X, który potencjalnie może być problem.

Istnieje również kilka informacji dotyczących XInitThreads pokrewnych() w

What is the downside of XInitThreads()?

pamiętać, że GDK używa blokad na wyświetlaczu, GTK/GDK nigdy nie XInitThreads połączeń.

Na marginesie: Co to jest ochrona zmienną globalną „onclick”, który jest przekazywana do execl po fork(), dziecko nie odziedziczy rodzica zamki pamięci i GLib MainLoop jest niezgodna z fork() . Może mógłbyś skopiować ciąg do zmiennej lokalnej.

+0

'gdk_threads_enter' i' gdk_threads_leave' są przestarzałe, więc ich używanie nie jest tak naprawdę opcją. Biorąc pod uwagę, że mam tylko jeden dodatkowy wątek, używanie puli wątków wydaje się nieco przesadzone, ale dzięki za wskazówkę! Wydaje się dziwne, że trzeba zadzwonić do 'XInitThreads' ręcznie, biorąc pod uwagę, że instrukcja mówi" System gwintowania GLIB jest automatycznie inicjowany na początku twojego programu ", ale postaram się go dzisiaj powtórzyć i złożyć raport. –

+1

Czy to nie dziwne, że trzeba zadzwonić do 'XInitThreads', gdy dokumentacja' gdk_threads_add_idle_full' mówi, że "wywołuje' funkcję' z zablokowaną GDK ", a dokumenty' XInitThreads' mówią: "Jeśli wszystkie połączenia do funkcji Xlib są chronione przez jakiś inny mechanizm dostępu (na przykład blokada wzajemnego wykluczenia w zestawie narzędzi lub poprzez jawne programowanie klienta), inicjalizacja wątku Xlib nie jest wymagana "? –

+0

[This] (https://github.com/Jonhoo/mktrayicon/commit/77a869fd4612c0cdffdee2f19ae5db3dda77adb6) naprawił problem, dziękuję! Nadal chciałbyś uzyskać więcej informacji na temat ** dlaczego ** to się dzieje dla nagrody. –

1

Nie jestem pewien, czy nagie pthreads są gwarantowane do pracy z GTK. Powinieneś użyć wrapperów GThread.

Myślę, że problemem może być to, że g_main_context_invoke() dodaje set_icon() jako bezczynną funkcję. (Wygląda na to, że dzieje się tak za kulisami, ale nie jestem pewien.) Funkcje bezczynności dodane za pomocą interfejsu API GLib, mimo że są wykonywane w głównym wątku, muszą posiadać blokadę GDK. Jeśli użyjesz interfejsu API gdk_threads_add_idle() (który nie jest przestarzały) do wywołania set_icon(), wówczas wszystko powinno działać poprawnie z wątkiem.

(Chociaż jest to tylko dzikie przypuszczenie.)

+0

Zmieniłem na GThreads i 'gdk_threads_add_idle', jak widać [w tym zatwierdzeniu] (https://github.com/Jonhoo/mktrayicon/commit/e62ede1cf0e863e0e9173d15f46a01bd6536fa11), ale nadal widzę błąd (lub niewielkie jego odmiany) czasami ... –

+0

Tak, GTK i PThreada są kompatybilne. – Lothar

0

Jeśli chcesz uniknąć blokowania interfejsu użytkownika podczas oczekiwania na niektóre IO, możesz użyć asynchronicznego IO z GIO. Unikałoby to konieczności samodzielnego zarządzania wątkami.

Edycja: Myśląc o tym, możesz po prostu oznaczyć deskryptory plików jako nieblokujące i dodać je jako źródło do głównej pętli glib, która będzie je odpytywać w pętli głównej zdarzenia bez konieczności zawracania sobie głowy wątkami .

+0

Ankietowanie nie wydaje się jednak szczególnie czystym rozwiązaniem. Wolałbym go zablokować, ponieważ oznaczałoby to, że program będzie reagował niemal natychmiast na polecenia. GIO wydaje się jednak interesujący. Czy mógłbyś podać przykład tego, jak mogę go wykorzystać? –

+0

Polling nie powinien być wolniejszy od odczytu blokującego, dlatego GTK reaguje na zdarzenia myszy i klawiatury. Aby to zrobić, utwórz 'GIOChannel' z' g_io_channel_new_file' i dodaj go do głównej pętli za pomocą 'g_io_add_watch'. Dla GIO utwórz 'GFile' z' g_file_new_for_commandline_arg' lub 'g_file_new_for_path', a następnie wywołaj' g_file_read_async' z funkcją zwrotną, która wywołuje 'g_file_read_finish', po której następuje' g_input_stream_read_async' z funkcją zwrotną, która wywołuje 'g_input_stream_read_finish', a następnie przetwarza odczytane dane, a następnie wywołania 'g_input_stream_read_async' ponownie. –

0

Można uniknąć używania wątków za pomocą funkcji gio_add_watch(), która wywoła funkcję zwrotną, gdy na kanale będą dostępne dane.

+0

Nie do końca banalne z plikami FIFO, ponieważ sposób, w jaki ich używam, będą musiały być ponownie otwarte (które blokuje) za każdym razem, gdy napotykamy EOF, gdy pisarze przychodzą i odchodzą. 'g_io_add_watch' wydaje się nie obsługiwać tego? Popraw mnie, jeśli się mylę. –