接上一篇文章https://blog.youkuaiyun.com/Master_Cui/article/details/109162220,上篇文章已经说过,在Ubuntu18.04,QT的事件机制实际上是采用glic中的事件循环机制来完成的,那么,QT是如果使用glib中的事件循环呢?
glib的主事件循环管理事件源。事件源可以通过g_source_attach()函数添加。每个事件源都会关联一个GMainContext。GMainLoop数据类型代表了一个主事件循环。通过g_main_loop_new()来创建GMainLoop对象。在添加完初始事件源后执行g_main_loop_run(),使得主线程进入循环,主循环将持续不断的检查每个事件源产生的新事件,然后分发它们,直到处理来自某个事件源的事件的时候触发了g_main_loop_quit()调用退出主循环为止。
执行g_main_context_iteration(d->mainContext, canWait)可以完成GMainContext的单次迭代。该函数会查看是否有事件源准备就绪,然后再处理,如果没有事件源准备就绪且canWait为true,则等待事件源准备就绪,然后调度优先级最高的事件源。 如果canWait为false,则不等待源准备就绪,只会分派已准备就绪的最高优先级事件源(如果有)
一次g_main_context_iteration包含g_main_context_prepare ()、g_main_context_query()、g_main_context_check()和g_main_context_dispatch()。
g_main_context_prepare:遍历_GMainContext拥有的事件源,调用事件源的_GSourceFuncs->prepare函数计算下次轮训间隔,并检测事件源是否已经就绪
g_main_context_query:从_GMainContext拥有的事件源中筛选出需要进行poll的事件源,并设置context->poll_changed为False
g_main_context_check:遍历g_main_context_query筛选出的事件源,调用事件源的_GSourceFuncs->check函数检测事件源是否已经就绪,如果就绪则将事件源添加到_GMainContext->pending_dispatches中。如果context->poll_changed为True(说明在poll的时候有新的事件源加入或移除),则跳过后面的分发(pending_dispatches为空),重新执行g_main_context_iterate
g_main_context_dispatch:遍历_GMainContext->pending_dispatches中的事件源,并调用事件源的_GSourceFuncs->dispatch函数进行分发
所以,本质上,glib的主事件循环机制实际上是使用Linux下的IO复用函数poll来实现的
首先要看下qeventdispatcher_glic.cpp中的部分代码
class Q_CORE_EXPORT QEventDispatcherGlibPrivate : public QAbstractEventDispatcherPrivate
{
public:
QEventDispatcherGlibPrivate(GMainContext *context = nullptr);
GMainContext *mainContext;
GPostEventSource *postEventSource;
GSocketNotifierSource *socketNotifierSource;
GTimerSource *timerSource;
GIdleTimerSource *idleTimerSource;
bool wakeUpCalled = true;
void runTimersOnceWithNormalPriority();
};
QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
: mainContext(context)
{

本文深入探讨了QT如何利用GLib的事件循环机制处理不同类型的事件,包括一般事件、网络事件及定时器事件。通过解析核心源码,揭示了QT在Ubuntu18.04系统上是如何高效地管理和分发事件。
最低0.47元/天 解锁文章
6498





