GTK3和多线程,取代已弃用的功能

时间:2015-06-02 22:05:20

标签: c multithreading gtk3 gdk

我想在使用线程的应用程序中替换已弃用的函数gdk_threads_enter()/leave()。现在的应用程序完美无缺(虽然我不确定这是否是正确的方法)。

我的主循环,运行gtk_main和信号处理程序。当我收到一个开始按钮时,我开始一个在主要背景中运行的线程。如何从该线程更新GUI。我知道根据GTK3和GDK3的文档,他们说使用

来避免它
gdk_threads_add_idle() 

gdk_threads_add_timeout() 

但是,如果我希望仅在单击“开始”时才进行更新,该怎么办呢? 有什么例子吗?我不是问如何使用gdk_threads_add_idle(),我问的是如何在点击开始后没有线程的情况下在main中运行worker函数。

点击了按钮 -​​ >先在线程中启动worker函数"" - >在GUI窗口中更新大量GUI元素。

3 个答案:

答案 0 :(得分:9)

文档说的是你仍然可以在一个线程中运行你的worker函数,你不能使用该线程中的GTK和GDK函数。因此,单击“开始”时仍可以启动该线程。但是,不必从线程更新GUI元素,而是必须使用gdk_threads_add_idle()安排从主线程更新它们。

所以你的图应该是这样的:

Main thread     Worker thread
    |
Button clicked
    |      \________
    |               \
    |           Start worker function
    |                |
    |           Computation
    |                |
    |           Want to update GUI
    |                |
    |           gdk_threads_add_idle(function1, data1)
    | ______________/|
    |/               |
    v           More computation
function1 runs       |
    |           Want to update GUI
GUI updated          |
    |           gdk_threads_add_idle(function2, data2)
    | ______________/|
    |/               |
    v           More computation
function2 runs       |
    |      
  etc...

如果这对于您的用例来说太复杂了,并且您的工作线程中的计算经常将控制返回到您的工作线程(比如,您在循环中计算某些内容),那么您可以完全运行计算在主线程中,没有通过简单地将控制权返回到GUI主循环来锁定GUI,如下所示:

for (lots of items) {
    result = do_short_calculation_on(one_item);

    update_gui(result);

    while (gtk_events_pending())
        gtk_main_iteration();
}

答案 1 :(得分:8)

您有3种方法可以做到:

  
      
  1. 在按钮回调中进行计算并使用gtk_event_pending() / gtk_main_iteration()

  2.   
  3. 使用g_idle_add()或其他人,gtk_event_pending() / gtk_main_iteration()

  4.   
  5. 使用线程,最终使用互斥锁,g_idle_add()或其他人。通常,不需要互斥锁,但它可以解决一些错误或   Heisenbugs

  6.   

第三个​​解决方案似乎是最好的,因为使用前两个方法,在计算运行时退出应用程序时遇到了一些问题。该应用程序没有退出并且正在打印很多" Gtk Critical "警告。 (我在Windows上尝试过它和mingw32 )。

1。按钮回调:

如果要在主gtk循环中运行工作线程,可以直接在按钮回调中进行计算,更新GUI并使用gtk_event_pending()gtk_main_iteration()处理来自它的事件,如在以下示例代码中:

void on_button_clicked(GtkButton * button, gpointer data) {

  // do some computation...

  // modify the GUI:
  gtk_label_set_text(label,"text");

  // run the main iteration to update the GUI,
  // you need to call these functions even if the GUI wasn't modified,
  // in order to get it responsive and treat events from it:
  while(gtk_events_pending()) gtk_main_iteration();

  // do some other computation...

  // huge computation in a loop:
  while(1) {
    // do some computation...

    // update the GUI and treat events from it:
    while(gtk_events_pending()) gtk_main_iteration();
  }
}

2。 g_idle_add():

您也可以使用代替g_thread_new()gdk_thread_add_idle()(如果某些不受您控制的库可能使用gdk_threads_enter()/leave())或g_idle_add()或{{1} }:

g_main_context_invoke()

3。线程和互斥:

使用线程的某些情况下,计算速度更快,因此在主gtk循环中使用工作线程时,以及在使用{添加到主循环的函数中更新GUI时来自工作线程的{1}}或gboolean compute_func(gpointer data) { // do some computation... // modify the GUI: gtk_label_set_text(label,"text"); // run the main loop to update the GUI and get it responsive: while(gtk_events_pending()) gtk_main_iteration(); // do some other computation... // huge computation in a loop: while(1) { // do some computation... // update GUI and treat events from it: while(gtk_events_pending()) gtk_main_iteration(); } return FALSE; } void on_button_clicked(GtkButton * button, gpointer data) { g_idle_add(compute_func,data); } ,您可能必须使用互斥锁来锁定对GUI的访问,因为访问GUI的函数之间可能存在冲突。在应用程序使用之前,必须使用gdk_threads_add_idle()初始化互斥锁。例如:

g_idle_add()

如果您需要更新GUI以按特定顺序执行的功能,则需要添加两个计数器并为使用g_mutex_init(&mutex_interface);GMutex mutex_interface; gboolean update_gui(gpointer data) { g_mutex_lock(&mutex_interface); // update the GUI here: gtk_button_set_label(button,"label"); // And read the GUI also here, before the mutex to be unlocked: gchar * text = gtk_entry_get_text(GTK_ENTRY(entry)); g_mutex_unlock(&mutex_interface); return FALSE; } gpointer threadcompute(gpointer data) { int count = 0; while(count <= 10000) { printf("\ntest %d",count); // sometimes update the GUI: gdk_threads_add_idle(update_gui,data); // or: g_idle_add(update_gui,data); count++; } return NULL; } void on_button_clicked(GtkButton * button, gpointer data) { g_thread_new("thread",threadcompute,data); } 调用的每个函数分配一个数字:

g_idle_add()

我还测试了锁定单个小部件而不是整个GUI的情况,它似乎有效。

答案 2 :(得分:0)

  

当我关闭主窗口时出现此运行错误:Gtk-CRITICAL   **:gtk_widget_get_parent:断言&#39; GTK_IS_WIDGET(小部件)&#39;失败

我想我找到了一个解决方案,使用两个全局变量来指示回调停止并调用gtk_main_quit(),并且已经陷入了#34; destroy&#34;在以下示例中,将主窗口信号转换为名为gtk_main_quit2()的自定义回调:

int process_running = 0; // indicate if the "process" is running
int stopprocess = 0; // indicate to the callback to stop or not

void gtk_main_quit2(GtkWidget * window, gpointer data) {
  if(process_running == 0) gtk_main_quit(); // if the "process" isn't running
                                            // then quit

  stopprocess = 1; // indicate to the button callback to stop and quit
}

void on_button_clicked(GtkButton * button, gpointer data) {

  // indicate the "process" is running:
  process_running = 1;

  // do some computation...


  while(gtk_events_pending()) gtk_main_iteration();
  if(stopprocess == 1) {
    // if close button clicked then quit:
    gtk_main_quit();
    return;
  }

  // do some other computation...

  // huge computation in a loop:
  while(1) {
    // do some computation...


    while(gtk_events_pending()) gtk_main_iteration();
    if(stopprocess == 1) {
      // if close button clicked then quit:
      gtk_main_quit();
      return;
    }
  }

  while(gtk_events_pending()) gtk_main_iteration();
  // indicate the "process" is finished:
  process_running = 0;
  // in the case the user clicked close button just at the end of computation:
  if(stopprocess == 1) {
    gtk_main_quit();
    return;
  }
}

int main() {

  gtk_init();
  Gtkwidget * window = create_window();
  g_signal_connect ((gpointer) window, "destroy", G_CALLBACK(gtk_main_quit2), NULL);
  gtk_main();

}

如果您在点击关闭按钮后仍然有一些gtk警告,您可以尝试捕获&#34;删除事件&#34;信号而不是&#34;销毁&#34;主窗口上的信号。