about gtk main loop

GTK+ 的主循环1

GTK+应用程序中,其主循环(main loop)更加简单,但是非常的不明了。

每个 gtk 应用程序都有下面这行代码:

gtk_main ();

不少人用GTK+写了很长时间的程序,还是觉得这行代码很神秘,不知道里面到底 干了什么。我们在这里分析下 gtk_main 的工作原理。

gtk_main 主要是对 glib 的 main loop 的包装,基本分为三步:

  1. 调用初始化函数;
  2. 进入glib main loop;
  3. 调用~初始化函数。

所以弄清楚glib main loop之后,gtk_main的实现也就尽收眼底了,这里重点分 析glib的main loop的实现。main loop使用模式大致如下:

loop = g_main_loop_new (NULL, TRUE);
g_main_loop_run (loop);
g_main_loop_quit ();

g_main_loop_new创建一个main loop对象,一个main loop对象只能被一个线程 使用,但一个线程可以有多个main loop对象。在GTK+应用中,一个线程使用多 个main loop的主要用途是实现模态对话框,它在gtk_dialog_run函数里创建一 个新的main loop,通过该main loop分发消息,直到对话框关闭为止。

g_main_loop_run则是进入主循环,它会一直阻塞在这里,直到让它退出为止。 有事件时,它就处理事件,没事件时就睡眠。

g_main_loop_quit则是用于退出主循环,相当于Win32下的PostQuitMessage函数。

Glib main loop 的最大特点就是支持多事件源,使用非常方便。来自用户的键盘 和鼠标事件、来自系统的定时事件和socket事件等等,还支持一个称为idle的事 件源,其主要用途是实现异步事件。

Glib Main loop的设计思想是基于 poll 的 "prepare/check/dispatch" 模式, 它的基本组成如下图所示:

来自资料1
来自资料1

GMainLoop的主要部件是GMainContext,GMainContext可以在多个GMainLoop间共 享,但要求这些GMainLoop都在同一个线程中运行,前面提到的模态对话框就属 于这一类。GMainContext通常由多个GSource组成,GSource是事件源的抽象,任 何事件源,只要实现GSource规定的接口,都可以挂到GMainContext中来。

GSource的接口函数有:

  1. gboolean (*prepare) (GSource *source, gint *timeout_);进入睡眠之前, 在g_main_context_prepare里,mainloop调用所有Source的prepare函数, 计算最小的timeout时间,该时间决定下一次睡眠的时间。
  2. gboolean (*check) (GSource *source); poll被唤醒后,在 g_main_context_check里,mainloop调用所有Source的check函数,检查是 否有Source已经准备好了。如果poll是由于错误或者超时等原因唤醒的,就 不必进行dispatch了。
  3. gboolean (*dispatch) (GSource*source, GSourceFunc callback,gpointer user_data); 当有Source准备好了,在 g_main_context_dispatch里,mainloop调用所有Source的dispatch函数, 去分发消息。
  4. void (*finalize) (GSource *source); 在Source被移出时,mainloop调用 该函数去销毁Source。

Main loop的工作流程简图如下:

来自资料1
来自资料1

下面我们看看几个内置Source的实现机制:

Idle 它主要用实现异步事件,功能类似于Win32下的PostMessage。但它还支持 重复执行的特性,根据用户注册的回调函数的返回值而定。

  1. g_idle_prepare把超时设置为0,也就是即时唤醒,不进入睡眠状态。
  2. g_idle_check 始终返回TRUE,表示准备好了。
  3. g_idle_dispatch 调用用户注册的回调函数。

Timeout 它主要用于实现定时器,支持一次定时和重复定时,根据用户注册的回调函数的返回值而定。

  1. g_timeout_prepare 计算下一次的超时时间。
  2. g_timeout_check 检查超时时间是否到了,如果到了就返回TRUE,否则返回FALSE。
  3. g_timeout_dispatch调用用户注册的回调函数。

线程可以向自己的mainloop中增加Source,也可以向其它线程的mainloop增加 Source。向自己的mainloop中增加Source时,mainloop已经唤醒了,所以不会存 在什么问题。而向其它线程的mainloop增加Source时,对方线程可能正挂在poll 里睡眠,所以要想法唤醒它,否则Source可能来不及处理。在Linux下,这是通 过wake_up_pipe管道实现的,mainloop在poll时,它除了等待所有的Source外, 还会等待wake_up_pipe管道。要唤醒poll,调用 g_main_context_wakeup_unlocked向wake_up_pipe里写入字母A就行了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值