2015-01-14 45 views
3

我有一個簡單的多線程Gtk + 2.0應用程序,它從多個來源(麥克風,攝像頭,溫度傳感器)獲取數據,並將這些數據顯示爲屏幕上的圖像(網絡攝像頭幀抓取,表示爲示波器呈現的麥克風數據,文本等)。在Gtk + 2.0應用程序中使用pthreads的安全性

這是根據我對Gtk手冊and various articles的理解,只有主處理線程應該使用任何影響UI的Gtk函數/調用。但是,在關閉UI之前,main()入口點將阻止gtk_main()。除了事件處理函數被映射成類似於當我點擊我的UI中的按鈕或滑塊時的事件處理函數之外,似乎唯一的選擇是讓我產生幾個pthread s,讓他們對數據進行定期採樣,在UI中更新屏幕上的信息。

我記得從做一些MFC GUI開發的一個很長的路上,應用了類似的原理:只有一個特定的線程應該更新UI元素。我如何在C中使用Gtk + 2.0來完成這項工作?

謝謝。

回答

5

根據該documentation,主甚至環可以從不同的線程接受來源:

甲GMainContext只能在單個線程中運行,但源可以 可以從添加到它併除去從它其他線程。

所以,你可以從你的工作線程注入在UI線程的代碼:

  1. 創建GSource(例如,通過使用g_idle_source_new);
  2. 將您想要執行的代碼添加到g-source-set-callback;
  3. g_source_attach()將它附加到UI線程上下文中。

這裏是GTK + 2和GLib的一個樣本程序> = 2.32:

#include <gtk/gtk.h> 

#define N_THREADS 100 
#define N_ITERATIONS 100 

GtkWidget *bar; 
GMainContext *context; 

static gboolean 
update_progress_bar(gpointer user_data) 
{ 
    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(bar), 
            g_random_double_range(0, 1)); 
    return G_SOURCE_REMOVE; 
} 


static gpointer 
thread_func(gpointer user_data) 
{ 
    int n_thread = GPOINTER_TO_INT(user_data); 
    int n; 
    GSource *source; 

    g_print("Starting thread %d\n", n_thread); 

    for (n = 0; n < N_ITERATIONS; ++n) { 
     /* If you want to see anything you should add a delay 
     * to let the main loop update the UI, e.g.: 
     * g_usleep(g_random_int_range(1234, 567890)); 
     */ 
     source = g_idle_source_new(); 
     g_source_set_callback(source, update_progress_bar, NULL, NULL); 
     g_source_attach(source, context); 
     g_source_unref(source); 
    } 

    g_print("Ending thread %d\n", n_thread); 
    return NULL; 
} 


gint 
main(gint argc, gchar *argv[]) 
{ 
    GtkWidget *window; 
    GThread *thread[N_THREADS]; 
    int n; 

    gtk_init(&argc, &argv); 

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); 

    bar = gtk_progress_bar_new(); 
    gtk_container_add(GTK_CONTAINER(window), bar); 

    context = g_main_context_default(); 

    for (n = 0; n < N_THREADS; ++n) 
     thread[n] = g_thread_new(NULL, thread_func, GINT_TO_POINTER(n)); 

    gtk_widget_show_all(window); 
    gtk_main(); 

    for (n = 0; n < N_THREADS; ++n) 
     g_thread_join(thread[n]); 

    return 0; 
} 
+0

你有如何做到這一點的例子用一個簡單的GTK + pthreads設置?謝謝! – DevNull

+1

@Dogbert剛剛添加到答案。 – ntd

+0

謝謝。這與我的原始問題意義最接近。 – DevNull

1

我會按照您的建議在單獨的線程中進行採樣。那麼問題是你如何更新UI。我會做的是使用'自我管道'。通常這是爲了與信號處理程序進行通信而完成的,但當其中一個線程無法在條件變量上等待時,它在線程間工作得很好。你在這裏做的是建立一個專用管道,在獲取數據的線程中向管道中寫入一個字符,並在主程序的select()循環的管道讀取端寫入select()。詳情在這裏:Using self-pipe, how can I avoid that the event loop stalls on read()? - 有用的背景源於它的起源here

然後,您可以讓GTK +在管道的讀取端進行偵聽。您的問題因此減少到使GTK +對FD上的某些內容做出響應 - 請參閱here(第一個答案)瞭解方法。

+0

我只熟悉GTK + 3是否GTK + 2沒有'gdk_threads_idle_add()'? – andlabs

+0

@andlabs我不知道這一點(而Google似乎也不知道這一點)。我想我正在回答一個關於如何在線程和爲傳統的非線程程序編寫的事件循環之間進行通信的更普遍的問題。 – abligh

+1

問題是,這並不能解決'main'線程在'gtk_main()'函數被阻塞時如何更新UI。單獨的線程可以更新UI嗎?我將所有UI更新調用都包含在互斥體中,因此不會有兩個線程同時嘗試更新UI。 – DevNull