2013-09-05 8 views
9

私はGTKを使用してかなり簡単にC applicationを構築していますが、いくつかのブロックIOを実行する必要があり、これによりGUIの更新がトリガーされます。これを行うためには、私は右のようなgtk_main()前に新しいpthreadを開始:pthreadは、いくつかのデータを読み取ると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(); 

、それはそうのようなGUIを更新します。

set_icon

ある
g_main_context_invoke(mainc, set_icon, param); 

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

これはほとんどすべての場合に機能しますが、何度も何度もこの不思議なエラーメッセージが表示されます。

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

g_main_context_invokeは、スレッドの問題を避けるためのものだと思いましたか?グーグルでやっているうちに、私はgdk_threads_initgdk_threads_enterと友だちに出くわしましたが、それらはすべて廃止されているようですね?私は、GTKのドキュメントでは、すべてのGUIの更新がメインスレッドで実行されるべきだと言いますが、これはIOをブロックするだけでよく、スレッド間に複雑な通信メカニズムを構築する必要はありません。

そして、私の質問は、どうすればこの問題に正しく対処すればよいのでしょうか?

EDIT:完全なコードは、here EDIT2を見ることができます:@のptomatoの回答に基づいて更新したように、私はGThread秒に移動し、gdk_threads_add_idle()を使用してthisに見られるようにコミットが、問題がまだ存在してきました。

+0

あなたはいくつかのコードを用意していますか?かなり長い間gtk +を使用していましたが、_never_はこの問題に遭遇しました。 – drahnr

+0

コード全体が[GitHub](https://github.com/jonhoo/mktrayicon) )をポストにリンクしています。 –

+0

ああ、感謝しなかった! – drahnr

答えて

8

コールXInitThreads()。これはメッセージを停止するgtk_initの前に行う必要があります!このような

何か:私は g_thread_init()/gdk_threads_init()を使用したGLIB 2.32、前にこれらのメッセージを見て覚えていない

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

g_thread_pool_newg_thread_pool_pushをチェックしてください。スレッドから 、ちょうど私がこれを簡単に確認することはできませんので、私は、トレイを使用していない

gdk_threads_enter()/ gdk_threads_leave()の間で糸を包むメインループまたは で実行するために g_main_context_invokeを使用しています。あなたはGTK/GDK APIを保護するためにロックを使用してgdk_threads_add_idleについて正しいとお考えになりました。 これらのメッセージに が表示されることは明らかです。 gtk_status_icon_new_from_icon_nameの機能の説明 は、「現在のアイコンテーマが変更された場合、アイコンは に適切に更新されます。あなたのコードはXディスプレイにアクセスするコードである コードだけではないことを意味します。 問題。

GDKは、表示のためにロックを使用しながら、GTK/GDKはない、これまで コールXInitThreadsを行うことを

What is the downside of XInitThreads()?

ノートでXInitThreads()に関するいくつかの関連情報もあります。サイドノートでは :フォーク(後execlのために渡され 「onclickの」グローバル変数を、)保護何、子供は親の メモリーロックを継承しません、とのGLibメインループは、フォークと互換性がありません() 。 おそらく、文字列をローカル変数にコピーすることができます。

+0

'gdk_threads_enter'と' gdk_threads_leave'は推奨されていないので、それらを使うのは本当にオプションではありません。私はスレッドプールを使用して1つだけ追加のスレッドを持っていることを考慮すると、少し過剰なようですが、チップのおかげで!マニュアルが「あなたのプログラムの開始時に自動的にGLIBスレッドシステムが初期化される」と言って、手動で 'XInitThreads'を呼び出さなければならないのは不思議です。 –

+1

また、gdk_threads_add_idle_full'のドキュメントでGDKロックを保持した状態で 'function'を呼び出すと、' XInitThreads'を呼び出さなければならないというのはちょっと奇妙なことではありませんし、 'XInitThreads'文書では" Xlib関数は、他のアクセス機構(例えば、ツールキットの相互排他ロックや明示的なクライアントプログラミングなど)によって保護されているため、Xlibスレッドの初期化は不要です。 –

+0

[This](https://github.com/Jonhoo/mktrayicon/commit/77a869fd4612c0cdffdee2f19ae5db3dda77adb6)は問題を修正しました。ありがとうございます! **についての情報はまだありますか?**なぜこれが恩恵のために起こっているのですか? –

1

裸のpthreadがGTKで動作することが保証されているかどうかはわかりません。 GThreadラッパーを使うべきです。

g_main_context_invoke()は、アイドル機能としてset_icon()を追加しています。 GLibのAPIを使って追加されたアイドル関数は、メインスレッドで実行されているにもかかわらず、GDKロックを保持する必要があります。 set_icon()を呼び出すためにgdk_threads_add_idle() API(推奨されていません)を使用している場合は、すべてがスレッド処理で正しく機能するはずです。

(これはちょうど野生の推測ですが。)

+0

私はGThreadsと 'gdk_threads_add_idle'を(このコミットでは)(https://github.com/Jonhoo/mktrayicon/commit/e62ede1cf0e863e0e9173d15f46a01bd6536fa11)見たように変更しましたが、まだエラーが出ています。時々... –

+0

はい、GTKとPThreadaは互換性があります。 – Lothar

0

回避策として、IOを待っている間にUIをブロックしたくない場合は、GIOの非同期IOを使用できます。そうすれば、自分でスレッドを管理する必要がなくなります。

編集:ファイル記述子をノンブロッキングとしてマークし、それらをglibメインループのソースとして追加するだけで、メインイベントループでそれらをポーリングすることができます。

+0

それをポーリングすると、特にきれいな解決策のようには見えません。私はむしろプログラムをかなりすぐにコマンドに反応することを意味するので、それをブロックするだろう。しかしGIOは面白そうだ。このために私がどのように使用するかの例を挙げてください。 –

+0

ポーリングはブロッキング読み出しよりも遅くするべきではなく、GTKがマウスやキーボードのイベントに反応するために使用します。これを行うには、 'g_io_channel_new_file'で' GIOChannel'を作成し、それを 'g_io_add_watch'でメインループに追加してください。 GIOの場合、 'g_file_new_for_commandline_arg'または' g_file_new_for_path'で 'GFile'を作成し、' g_file_read_finish'をコールバックし、 'g_input_stream_read_async'をコールバックし、' g_input_stream_read_finish'をコールバックしたコールバックで 'g_file_read_async'を呼び出して、 'g_input_stream_read_async'を再度実行してください。 –

0

gio_add_watch()を使用してスレッドを使用することは避けることができます。これは、チャネルで利用できるデータがあるときにコールバック関数を呼び出すことになります。

+0

FIFOファイルではそれほど些細なことではありません。私はそれらを使用しているので、ライターの出入りがあるたびにEOFに遭遇するたびに再オープンする必要があります。 'g_io_add_watch'はこれをサポートしていないようですか?私が間違っているなら私を訂正してください。 –

関連する問題