c - GTK3 和多线程,替换已弃用的函数

标签 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 函数。

单击按钮 --> “在先前的线程中”启动辅助功能 --> 在 GUI 窗口中更新大量 GUI 元素。

最佳答案

您有 3 种方法可以做到这一点:

  1. make computation in the button callback and use gtk_event_pending()/gtk_main_iteration()

  2. use g_idle_add() or others, and gtk_event_pending()/gtk_main_iteration()

  3. use a thread, eventually a mutex, and g_idle_add() or others. Normally, a mutex isn't needed but it may solve some bugs or Heisenbugs.

第三种 解决方案似乎是最好的,因为使用前两种 方法时,我在计算运行时退出应用程序时遇到了一些问题。该应用程序没有退出,并且正在打印大量“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()g_main_context_invoke():

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);
}

3。线程和互斥:

某些情况下,使用线程会使计算速度更快,因此当使用不在主 gtk 循环中的工作线程时,以及在添加到主循环的函数中更新 GUI 时,gdk_threads_add_idle()g_idle_add(),您可能必须使用互斥锁锁定对 GUI 的访问,因为访问 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);
}

如果您需要更新 GUI 的函数以特定顺序执行,您需要添加两个计数器并为使用 g_idle_add()gdk_threads_add_ilde 调用的每个函数分配一个编号():

GMutex mutex_interface;

typedef struct _data DATA;
struct _data {
  gchar label[1000];
  GtkWidget * w;
  int num;
};


int counter = 0;
int counter2 = 0;

gboolean update_gui(gpointer data) {
  DATA * d = (DATA *)data;

  debutloop:
  g_mutex_lock(&mutex_interface);
  if(d->num != counter2) {
    g_mutex_unlock(&mutex_interface);
    goto debutloop;
  }
  counter2++;
  // update the GUI here:
  gtk_button_set_label(GTK_BUTTON(d->w),d->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);

  free(d);

  return FALSE;
}

gpointer threadcompute(gpointer data) {
  int count = 0;

  while(count <= 10000) {
    printf("\ntest %d",count);

    DATA * d = (DATA*)malloc(sizeof(DATA));
    sprintf(d->label,"%d",count);
    d->w = (GtkWidget*)data;
    d->num = counter;
    counter++;
    // update the GUI:
    g_idle_add(update_gui,d);

    count++;
  }
  return NULL;
}

void on_button_clicked(GtkButton * button, gpointer data) {

    g_thread_new("thread",threadcompute,button);
}

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

关于c - GTK3 和多线程,替换已弃用的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30607429/

相关文章:

c++ - 多线程文件读取每个线程产生相同的结果

java - 如何防止 Android 应用程序在每次手机 hibernate 时重置?

Codeblocks GTK 项目未找到 gtk.h 并且此路径不包含 include 或 lib 文件夹

python - 如何使用 Python 在 Gtk3 中显示 png 图像?

c - SDL_WINDOWEVENT_ROTATE 结构

c - 如何使用 libpng 将 C 代码中的 png 图像解码为原始字节?

计算圆形数量级

multithreading - 不稳定的__thread,毫无意义?

c - 结构数组 : Incompatible parameter types

python - 如何在 Linux 中用 Python 捕获 gtk ApplicationWindow 的图像?