2013-09-05 35 views
9

Tôi đang xây dựng khá đơn giản C application bằng GTK, nhưng phải thực hiện một số IO chặn sẽ kích hoạt cập nhật cho GUI. Để làm được điều này, tôi bắt đầu một pthread đúng mới trước khi gtk_main() như vậy:Vấn đề về luồng với 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(); 

Khi pthread đọc một số dữ liệu, nó cập nhật GUI như vậy:

g_main_context_invoke(mainc, set_icon, param); 

đâu set_icon

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

Điều này tất cả hoạt động hầu hết thời gian, nhưng hiện tại tôi nhận được thông báo lỗi tò mò này:

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

Tôi nghĩ rằng toàn bộ điểm sử dụng g_main_context_invoke là để tránh các vấn đề với chủ đề? Làm một chút Googling, tôi đi qua gdk_threads_init, gdk_threads_enter và bạn bè, nhưng tất cả chúng dường như không được chấp nhận? Tôi biết tài liệu GTK nói rằng tất cả các cập nhật GUI nên được thực hiện trên luồng chính, nhưng điều này không kết hợp tốt với việc chặn IO, và tôi không muốn xây dựng một số cơ chế giao tiếp phức tạp giữa các luồng.

Và vì vậy, câu hỏi của tôi là, tôi nên xử lý chính xác điều này như thế nào?

EDIT: Mã đầy đủ có thể được nhìn thấy here EDIT2: Như một bản cập nhật dựa trên câu trả lời @ ptomato, tôi đã chuyển sang GThread s và sử dụng gdk_threads_add_idle() như đã thấy trong this cam kết, nhưng vấn đề vẫn còn hiện diện.

+0

Bạn có sẵn một số mã không, tôi đã sử dụng gtk + trong một thời gian và _never_ đã gặp vấn đề này .. – drahnr

+0

Toàn bộ mã có sẵn tại [GitHub] (https://github.com/jonhoo/mktrayicon) như được liên kết trong bài đăng. –

+0

Ah, nhớ lời cảm ơn đó! – drahnr

Trả lời

8

Gọi XInitThreads(). Điều này nên được thực hiện trước gtk_init, điều này sẽ dừng các tin nhắn!

Something như thế này:

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

Tôi không nhớ nhìn thấy những tin nhắn trước khi GLib 2.32, khi g_thread_init()/gdk_threads_init() được sử dụng.

Bạn có thể muốn xem g_thread_pool_newg_thread_pool_push. Từ chủ đề, sử dụng g_main_context_invoke để thực hiện trong vòng lặp chính hoặc chỉ quấn chủ đề giữa gdk_threads_enter()/gdk_threads_leave()

tôi không sử dụng một khay vì vậy tôi không thể dễ dàng kiểm tra điều này.Tôi nghĩ rằng bạn đang chính xác về gdk_threads_add_idle sử dụng khóa để bảo vệ GTK/GDK API. Không có gì rõ ràng với tôi rằng có thể khiến các thông báo này xuất hiện với số . Mô tả chức năng cho gtk_status_icon_new_from_icon_name nói rằng "Nếu chủ đề biểu tượng hiện tại được thay đổi, biểu tượng sẽ là được cập nhật một cách thích hợp. Tôi cho rằng mã của bạn không phải là mã duy nhất sẽ truy cập màn hình X, có khả năng là vấn đề.

Ngoài ra còn có một số thông tin liên quan về XInitThreads() tại

What is the downside of XInitThreads()?

Lưu ý rằng trong khi GDK sử dụng ổ khóa cho màn hình hiển thị, GTK/GDK đừng bao giờ XInitThreads gọi.Trên một mặt lưu ý: Điều gì đang bảo vệ biến toàn cầu "onclick", mà được chuyển đến execl sau ngã ba(), đứa trẻ sẽ không kế thừa khóa của bộ nhớ của cha mẹ và GLib mainloop không tương thích với fork() . Có thể bạn có thể sao chép chuỗi thành biến cục bộ.

+0

'gdk_threads_enter' và' gdk_threads_leave' đã không được chấp nhận, vì vậy việc sử dụng chúng không thực sự là một tùy chọn. Xem xét tôi chỉ có một chủ đề bổ sung, sử dụng một hồ bơi thread có vẻ hơi quá mức cần thiết, nhưng cảm ơn cho tip! Có vẻ lạ khi phải gọi 'XInitThreads' theo cách thủ công xem xét hướng dẫn sử dụng" Hệ thống luồng GLib được tự động khởi chạy khi bắt đầu chương trình của bạn ", nhưng tôi sẽ cho nó đi sau ngày hôm nay và báo cáo lại. –

+1

Ngoài ra, có hơi lạ khi phải gọi 'XInitThreads' khi tài liệu cho' gdk_threads_add_idle_full' nói rằng nó "gọi' hàm' với khóa GDK được giữ ", và các tài liệu' XInitThreads' nói "Nếu tất cả các cuộc gọi thế nào để chức năng Xlib được bảo vệ bởi một số cơ chế truy cập khác (ví dụ, một khóa loại trừ lẫn nhau trong một bộ công cụ hoặc thông qua lập trình khách hàng rõ ràng), Xlib thread khởi tạo là không cần thiết "? –

+0

[This] (https://github.com/Jonhoo/mktrayicon/commit/77a869fd4612c0cdffdee2f19ae5db3dda77adb6) khắc phục sự cố, cảm ơn bạn! Sẽ vẫn còn một số thông tin khác về ** lý do tại sao ** điều này xảy ra với tiền thưởng. –

1

Tôi không chắc chắn nếu các pthread trần được đảm bảo để làm việc với GTK. Bạn nên sử dụng trình bao bọc GThread.

Tôi nghĩ rằng vấn đề có thể là g_main_context_invoke() đang thêm set_icon() làm chức năng không hoạt động. (Có vẻ như đó là những gì diễn ra đằng sau hậu trường, nhưng tôi không chắc chắn.) Các chức năng nhàn rỗi được thêm vào bằng cách sử dụng API của GLib, mặc dù được thực hiện trên chủ đề chính, cần giữ khóa GDK. Nếu bạn sử dụng API gdk_threads_add_idle() (không được dùng nữa) để gọi set_icon(), thì mọi thứ sẽ hoạt động bình thường với luồng.

(Mặc dù đây chỉ là phỏng đoán hoang dã.)

+0

Tôi đã thay đổi thành GThreads và 'gdk_threads_add_idle' như đã thấy [trong cam kết này] (https://github.com/Jonhoo/mktrayicon/commit/e62ede1cf0e863e0e9173d15f46a01bd6536fa11), nhưng tôi vẫn thấy lỗi (hoặc các biến thể nhỏ của nó) thỉnh thoảng ... –

+0

Có, GTK và PThreada tương thích. – Lothar

0

Là một công việc xung quanh, nếu bạn chỉ muốn tránh chặn giao diện người dùng trong khi chờ IO, bạn có thể sử dụng IO không đồng bộ từ GIO. Điều đó sẽ tránh bạn phải tự quản lý chủ đề.

Chỉnh sửa: Suy nghĩ về nó bạn chỉ có thể đánh dấu các mô tả tệp của bạn là không chặn và thêm chúng dưới dạng nguồn vào vòng lặp chính glib và nó sẽ thăm dò ý kiến ​​cho bạn trong vòng lặp sự kiện chính mà không cần phải bận tâm về chủ đề .

+0

Thăm dò ý kiến ​​nó không có vẻ giống như một giải pháp đặc biệt sạch mặc dù. Tôi muốn chặn nó vì điều đó có nghĩa là chương trình sẽ phản ứng khá nhiều ngay lập tức với các lệnh. GIO có vẻ thú vị. Bạn có thể đưa ra một ví dụ về cách tôi có thể sử dụng nó cho điều này? –

+0

Việc bỏ phiếu không nên chậm hơn so với việc đọc chặn, đó là những gì GTK sử dụng để phản ứng với các sự kiện chuột và bàn phím. Để thực hiện điều này, hãy tạo 'GIOChannel' bằng' g_io_channel_new_file' và thêm nó vào vòng lặp chính với 'g_io_add_watch'. Đối với GIO, tạo một 'GFile' với' g_file_new_for_commandline_arg' hoặc 'g_file_new_for_path' rồi gọi' g_file_read_async' với một cuộc gọi lại gọi 'g_file_read_finish' theo sau bởi' g_input_stream_read_async' với một cuộc gọi lại gọi 'g_input_stream_read_finish' và sau đó xử lý dữ liệu đọc và sau đó gọi 'g_input_stream_read_async' một lần nữa. –

0

Bạn có thể tránh sử dụng các chuỗi bằng cách sử dụng gio_add_watch() sẽ gọi hàm gọi lại của bạn khi có dữ liệu trên kênh.

+0

Không hoàn toàn tầm thường với các tệp FIFO, bởi vì cách tôi đang sử dụng chúng, chúng sẽ cần được mở lại (ngăn chặn) mỗi khi EOF gặp phải khi các nhà văn đến và đi. 'g_io_add_watch' có vẻ không hỗ trợ điều này? Đúng nếu tôi sai. –

Các vấn đề liên quan