基于C语言中的“quit”标志正确终止GLib主循环

4

我知道这可能是一个新手GLib问题,但我很难找到解决我的问题的代码示例。所以在我走错路之前,我想听听您的建议。

我的代码监听D-Bus消息。其中一个D-Bus消息是“Quit”消息,旨在指示主循环关闭。如果主循环中没有其他任务,则可以在下面的代码中简单地调用g_main_loop_run(),并且当接收到“Quit”消息时(此处未显示)执行g_main_loop_quit()的D-Bus消息处理代码来实现解决方案。

然而,我更喜欢由主循环做出退出的决定,这样可以执行除了监听D-Bus消息以外的各种其他任务。以下代码在设置工作中的D-Bus服务器后按预期执行此操作:

GMainLoop *glib_mainloop = g_main_loop_new( NULL, false );
/* Set up the D-Bus connection to work in the GLib event loop. */
dbus_connection_setup_with_g_main( dbus_connection, NULL );

/* Run the GLib event loop. */
GMainContext *glib_context = g_main_context_default( );
while( quit_indicator == false )
{
    g_main_context_iteration( glib_context, /*allow blocking=*/false );
    /* Do a variety of other tasks. */
}

g_main_loop_quit( glib_mainloop );

注意:上述代码是为了阐明我的问题而提供的最少量代码,我知道主循环中的“其他任务”可能更好地由线程、GSource或其他方式处理。例如,如果上述代码直接使用,会出现明显的繁忙等待或时间问题。
我的问题是:上面的代码示例是否是实现我的目标的正确方式,还是有更“真正”的GLib方法可以做到这一点?

1
这似乎更适合在http://codereview.stackexchange.com/上进行。 - Some programmer dude
1个回答

6

您的方法基本正确。许多示例都建议使用 g_main_loop_run()g_main_loop_quit() 来控制主上下文,但像您这样手动迭代主上下文更清晰。

代码需要做的一项更改是告诉 g_main_context_iteration() 允许阻塞,否则您的主循环实际上就是一个忙循环,而且在等待 I/O 时进程不会休眠。如果手动迭代 GMainContext,您完全不需要使用 GMainLoop

另一项必要的更改是在更改终止条件值时,在 g_main_context_iteration() 中调用 g_main_context_wakeup() 来唤醒主上下文,以便从阻塞状态中退出。

第三项修改是删除使用 g_main_loop_new()/g_main_loop_quit() 创建和退出 GMainLoop 的代码,因为该 GMainLoop 没有使用 g_main_loop_run() 运行。

以下是更新后的代码:

GMainContext *context = g_main_context_default ();
gboolean quit = FALSE;

/* Set up the D-Bus connection to work in the GLib event loop. */
dbus_connection_setup_with_g_main (dbus_connection, context);

/* Run the GLib event loop. */
while (!quit)
  g_main_context_iteration (context, TRUE);

/* To exit the main loop (from a callback implementing your D-Bus Quit() function): */
quit = TRUE;
g_main_context_wakeup (NULL  /* global default main context */);

还有几点需要注意:

  • 你所提到的“其他任务”应该在主上下文的回调函数中执行,正如你所指出的。
  • 使用GDBus而不是dbus-glib(已被弃用且未维护)。我在这里写了一个答案,讲解为什么以及如何选择D-Bus绑定。
  • 如果你要在新线程中执行此操作(而不是在主线程),你应该使用新的GMainContextg_autoptr(GMainContext) context = g_main_context_new (); g_main_context_push_thread_default(context);。你不应该在多个线程之间共享GMainContext。虽然做到这一点是安全的,但并不高效。

这是一个使用这些技术的基于现代GLib的守护进程中的主循环的MPL许可示例:https://git.apertis.org/cgit/rhosydd.git/tree/libcroesor/service.c#n569


太好了!我肯定错过了g_main_context_wakeup()。我知道dbus-glib已经过时,但迄今为止我还没有找到可工作的GDBus示例。 - Ole Wolf
在GIO测试中有很多GDBus示例。其中一些链接在文档中,但其他示例也很有用。请在以下文件中查找“example”:https://git.gnome.org/browse/glib/tree/gio/tests - Philip Withnall
1
真的,真的不要再使用dbus-glib了。它已经不受支持,并且与其他东西集成起来很麻烦。 - Philip Withnall
我最终按照你的建议迁移到了GDBus。在开发主机上,我已经成功地运行了dbus-glib,但是部署目标设备使用了一个略有不同的Linux发行版,似乎包含了一个有缺陷的dbus-glib版本。幸运的是,迁移到GDBus只花了几个小时,从对GDBus一无所知到将其与剩余代码集成。 - Ole Wolf
很高兴听到这个消息! - Philip Withnall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接