web-dev-qa-db-ja.com

GTKのスレッドの問題

私はかなり単純な Cアプリケーション をGTKを使用して構築していますが、GUIの更新をトリガーするブロッキングIOを実行する必要があります。これを行うには、 gtk_main()の直前に新しいpthreadを開始します。

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

pthreadがデータを読み取ると、次のようにGUIが更新されます。

_g_main_context_invoke(mainc, set_icon, param);
_

_set_icon_は

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

これはほとんどの場合機能しますが、時々、この奇妙なエラーメッセージが表示されます。

 [xcb]キューの処理中にシーケンス番号が不明です
 [xcb]これはマルチスレッドクライアントであり、XInitThreadsが呼び出されていない可能性があります
 [xcb]中止、申し訳ありません。
 mktrayicon:xcb_io.c:274:poll_for_event:アサーション `!xcb_xlib_threads_sequence_lost 'が失敗しました。

_g_main_context_invoke_を使用することの全体的なポイントは、スレッドの問題を回避することだと思いましたか?グーグルを少しやってみると、_gdk_threads_init_、_gdk_threads_enter_とその友達に出くわしましたが、それらはすべて非推奨のようですか? GTKのドキュメントには、すべてのGUI更新はメインスレッドで実行する必要があると記載されていますが、これはIOのブロックとうまく組み合わされていないため、スレッド間に複雑な通信メカニズムを構築する必要はありません。

だから、私の質問は、これに正しく対処するにはどうすればよいですか?

編集:完全なコードを見ることができます ここ 編集2:@ptomatoの回答に基づく更新として、私はGThreadsに移動し、gdk_threads_add_idle()を使用しています。 this commitですが、問題はまだ存在します。

13
Jon Gjengset

XInitThreads()を呼び出します。これは_gtk_init_の前に実行する必要があります。これにより、メッセージが停止します。

このようなもの:

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

g_thread_init()/gdk_threads_init()が使用されたときに、GLIB2.32より前にこれらのメッセージが表示されたのを覚えていません。

_g_thread_pool_new_と_g_thread_pool_Push_をチェックすることをお勧めします。スレッドから、_g_main_context_invoke_を使用してメインループで実行するか、gdk_threads_enter()/gdk_threads_leave()の間でスレッドをラップします。

トレーを使わないのでなかなか確認できません。 GTK/GDKAPIを保護するためにロックを使用するgdk_threads_add_idleについては正しいと思います。これらのメッセージが表示される原因となる明らかなことは何もありません。 gtk_status_icon_new_from_icon_nameの関数の説明には、「現在のアイコンテーマが変更されると、アイコンが適切に更新されます。これは、Xディスプレイにアクセスするコードがコードだけではないことを意味し、問題が発生する可能性があります。

XInitThreads()に関するいくつかの関連情報もあります。

XInitThreads()の欠点は何ですか?

GDKはディスプレイにロックを使用しますが、GTK/GDKはXInitThreadsを呼び出さないことに注意してください。

補足:fork()の後にexeclに渡されるグローバル変数 "onclick"を保護しているのは、子が親のメモリロックを継承せず、GLibメインループがfork()と互換性がないことです。文字列をローカル変数にコピーできるかもしれません。

11
Wiley

ベアpthreadがGTKで動作することが保証されているかどうかはわかりません。 GThreadラッパーを使用する必要があります。

問題は、g_main_context_invoke()がアイドル関数としてset_icon()を追加していることだと思います。 (それが舞台裏で行われているようですが、よくわかりません。)GLibのAPIを使用して追加されたアイドル関数は、メインスレッドで実行されているにもかかわらず、GDKロックを保持する必要があります。 gdk_threads_add_idle() AP​​I(非推奨ではありません)を使用してset_icon()を呼び出す場合、すべてがスレッド化で正しく機能するはずです。

(これは単なる推測ですが。)

1
ptomato

回避策として、いくつかのIOを待っている間にUIをブロックしないようにしたい場合は、非同期IO from [〜 #〜] gio [〜#〜] 。これにより、スレッドを自分で管理する必要がなくなります。

編集:それについて考えると、ファイル記述子を非ブロッキングとしてマークし、それらをソースとしてglibメインループに追加するだけで、スレッドをいじることなくメインイベントループでそれらをポーリングします。

0
Phillip Wood

チャネルで利用可能なデータがある場合にコールバック関数を呼び出すgio_add_watch()を使用することで、スレッドの使用を回避できます。

0
barry day