接上一篇文章https://blog.csdn.net/Master_Cui/article/details/109162220,上篇文章已经说过,在Ubuntu18.04,QT的事件机制实际上是采用glic中的事件循环机制来完成的,那么,QT是如果使用glib中的事件循环呢?

1、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)
{
#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32if (qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB")) {static QBasicMutex mutex;QMutexLocker locker(&mutex);if (!g_thread_supported())g_thread_init(NULL);}
#endifif (mainContext) {//根据mainContext是否为空创建新的mainContextg_main_context_ref(mainContext);} else {QCoreApplication *app = QCoreApplication::instance();if (app && QThread::currentThread() == app->thread()) {mainContext = g_main_context_default();g_main_context_ref(mainContext);} else {mainContext = g_main_context_new();}}#if GLIB_CHECK_VERSION (2, 22, 0)g_main_context_push_thread_default (mainContext);//该Context作为线程的默认Context,并使用主事件循环侦听;
#endif// setup post event sourcepostEventSource = reinterpret_cast<GPostEventSource *>(g_source_new(&postEventSourceFuncs,sizeof(GPostEventSource)));//设置发送事件源的相关操作postEventSource->serialNumber.storeRelaxed(1);postEventSource->d = this;g_source_set_can_recurse(&postEventSource->source, true);g_source_attach(&postEventSource->source, mainContext);//将发送事件源添加到mainContext中// setup socketNotifierSourcesocketNotifierSource =reinterpret_cast<GSocketNotifierSource *>(g_source_new(&socketNotifierSourceFuncs,sizeof(GSocketNotifierSource)));//设置发送网络事件源的相关操作(void) new (&socketNotifierSource->pollfds) QList<GPollFDWithQSocketNotifier *>();g_source_set_can_recurse(&socketNotifierSource->source, true);g_source_attach(&socketNotifierSource->source, mainContext);将发送网络事件源添加到mainContext中// setup normal and idle timer sourcestimerSource = reinterpret_cast<GTimerSource *>(g_source_new(&timerSourceFuncs,sizeof(GTimerSource)));(void) new (&timerSource->timerList) QTimerInfoList();timerSource->processEventsFlags = QEventLoop::AllEvents;timerSource->runWithIdlePriority = false;g_source_set_can_recurse(&timerSource->source, true);g_source_attach(&timerSource->source, mainContext);idleTimerSource = reinterpret_cast<GIdleTimerSource *>(g_source_new(&idleTimerSourceFuncs,sizeof(GIdleTimerSource)));idleTimerSource->timerSource = timerSource;g_source_set_can_recurse(&idleTimerSource->source, true);g_source_attach(&idleTimerSource->source, mainContext);
}QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent): QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
{
}QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent): QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
{ }

所以,当初始化一个QEventDispatcherGlib对象时,会在QEventDispatcherGlibPrivate中将发送一般事件源、网络事件源和定时器事件的源添加到glic事件循环中的mainContext,这样,当对mainContext进行迭代时,就会依次执行g_main_context_prepare,g_main_context_query,g_main_context_check和g_main_context_dispatch,并调用对应的source的函数

对应的source函数的实现如下,源码也在qeventdispatcher_glic.cpp中

static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
{if (timeout)*timeout = -1;return false;
}static gboolean socketNotifierSourceCheck(GSource *source)
{GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);bool pending = false;for (int i = 0; !pending && i < src->pollfds.count(); ++i) {GPollFDWithQSocketNotifier *p = src->pollfds.at(i);if (p->pollfd.revents & G_IO_NVAL) {// disable the invalid socket notifierstatic const char *t[] = { "Read", "Write", "Exception" };qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...",p->pollfd.fd, t[int(p->socketNotifier->type())]);// ### note, modifies src->pollfds!p->socketNotifier->setEnabled(false);i--;} else {pending = pending || ((p->pollfd.revents & p->pollfd.events) != 0);}}return pending;
}static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpointer)
{QEvent event(QEvent::SockAct);GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);for (int i = 0; i < src->pollfds.count(); ++i) {GPollFDWithQSocketNotifier *p = src->pollfds.at(i);if ((p->pollfd.revents & p->pollfd.events) != 0)QCoreApplication::sendEvent(p->socketNotifier, &event);}return true; // ??? don't remove, right?
}static GSourceFuncs socketNotifierSourceFuncs = {socketNotifierSourcePrepare,socketNotifierSourceCheck,socketNotifierSourceDispatch,NULL,NULL,NULL
};static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
{timespec tv = { 0l, 0l };if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv))*timeout = (tv.tv_sec * 1000) + ((tv.tv_nsec + 999999) / 1000 / 1000);else*timeout = -1;return (*timeout == 0);
}static gboolean timerSourceCheckHelper(GTimerSource *src)
{if (src->timerList.isEmpty()|| (src->processEventsFlags & QEventLoop::X11ExcludeTimers))return false;if (src->timerList.updateCurrentTime() < src->timerList.constFirst()->timeout)return false;return true;
}static gboolean timerSourcePrepare(GSource *source, gint *timeout)
{gint dummy;if (!timeout)timeout = &dummy;GTimerSource *src = reinterpret_cast<GTimerSource *>(source);if (src->runWithIdlePriority) {if (timeout)*timeout = -1;return false;}return timerSourcePrepareHelper(src, timeout);
}static gboolean timerSourceCheck(GSource *source)
{GTimerSource *src = reinterpret_cast<GTimerSource *>(source);if (src->runWithIdlePriority)return false;return timerSourceCheckHelper(src);
}static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer)
{GTimerSource *timerSource = reinterpret_cast<GTimerSource *>(source);if (timerSource->processEventsFlags & QEventLoop::X11ExcludeTimers)return true;timerSource->runWithIdlePriority = true;(void) timerSource->timerList.activateTimers();return true; // ??? don't remove, right again?
}static GSourceFuncs timerSourceFuncs = {timerSourcePrepare,timerSourceCheck,timerSourceDispatch,NULL,NULL,NULL
};static gboolean idleTimerSourcePrepare(GSource *source, gint *timeout)
{GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);GTimerSource *timerSource = idleTimerSource->timerSource;if (!timerSource->runWithIdlePriority) {// Yield to the normal priority timer sourceif (timeout)*timeout = -1;return false;}return timerSourcePrepareHelper(timerSource, timeout);
}static gboolean idleTimerSourceCheck(GSource *source)
{GIdleTimerSource *idleTimerSource = reinterpret_cast<GIdleTimerSource *>(source);GTimerSource *timerSource = idleTimerSource->timerSource;if (!timerSource->runWithIdlePriority) {// Yield to the normal priority timer sourcereturn false;}return timerSourceCheckHelper(timerSource);
}static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)
{GTimerSource *timerSource = reinterpret_cast<GIdleTimerSource *>(source)->timerSource;(void) timerSourceDispatch(&timerSource->source, 0, 0);return true;
}static GSourceFuncs idleTimerSourceFuncs = {idleTimerSourcePrepare,idleTimerSourceCheck,idleTimerSourceDispatch,NULL,NULL,NULL
};static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
{QThreadData *data = QThreadData::current();if (!data)return false;gint dummy;if (!timeout)timeout = &dummy;const bool canWait = data->canWaitLocked();*timeout = canWait ? -1 : 0;GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);source->d->wakeUpCalled = source->serialNumber.loadRelaxed() != source->lastSerialNumber;return !canWait || source->d->wakeUpCalled;
}static gboolean postEventSourceCheck(GSource *source)
{return postEventSourcePrepare(source, 0);
}static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
{GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);source->lastSerialNumber = source->serialNumber.loadRelaxed();QCoreApplication::sendPostedEvents();//将所有事件发送给所有对象source->d->runTimersOnceWithNormalPriority();return true; // i dunno, george...
}static GSourceFuncs postEventSourceFuncs = {postEventSourcePrepare,postEventSourceCheck,postEventSourceDispatch,NULL,NULL,NULL
};

上述代码中,postEventSourcePrepare,idleTimerSourcePrepare,timerSourcePrepare和socketNotifierSourcePrepare主要用来设置poll机制的超时时间;postEventSourceCheck,idleTimerSourceCheck,timerSourceCheck和socketNotifierSourceCheck主要是用来检测各个源中是否有事件准备就绪,而postEventSourceDispatch,idleTimerSourceDispatch,timerSourceDispatch和socketNotifierSourceDispatch主要是用来发送事件,其中idleTimerSourceDispatch,timerSourceDispatch和socketNotifierSourceDispatch这三个函数发送事件时,调用的是QCoreApplication::sendEvent,而postEventSourceDispatch发送事件时,调用的是QCoreApplication::sendPostedEvents()。这两个函数的区别见博客https://blog.csdn.net/Master_Cui/article/details/109109972

也就是说,QT下的事件机制不会将网络事件和定时器事件放在事件队列中,而是直接指定接收者接受,而一般事件会放在一个事件发送队列里,然后调用sendPostedEvents一起发送

参考

Qt5.14源码

https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html

https://www.cnblogs.com/silvermagic/p/9087881.html

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

10.QT事件机制源码时序分析(中)相关推荐

  1. 11.QT事件机制源码时序分析(下)

    接上一篇文章https://blog.csdn.net/Master_Cui/article/details/109182406,本文继续解析QCoreApplication::sendEvent和Q ...

  2. 9.QT事件机制源码时序分析(上)

    通过上两篇博客https://blog.csdn.net/Master_Cui/article/details/109093845和https://blog.csdn.net/Master_Cui/a ...

  3. Qt 事件机制源码分析 QApplication exec 源码分析 多图超级详细

    前言: 不熟悉qt 源码结构的 可以先看这一篇 点我点我点我 写qt 的都知道 以下代码, 这段代码究竟的运行机制是怎么样的 咱们一步一步的看 QApplication a(argc, argv);Q ...

  4. React事件机制 - 源码概览(下)

    上篇文档 React事件机制 - 源码概览(上)说到了事件执行阶段的构造合成事件部分,本文接着继续往下分析 批处理合成事件 入口是 runEventsInBatch // runEventsInBat ...

  5. 【保姆级】react17 事件机制源码解析

    写在前面 react17是一个过渡版本,最大的改动莫过于针对_事件机制的重构_.虽然现在react18版本已经发布了,但对事件机制这部分几乎是没有改动的,因此这里依然可以对17版本的这部分源码作一次解 ...

  6. Qt 事件机制,底层实现原理

    [1]事件 事件是可以被控件识别的操作.如按下确定按钮.选择某个单选按钮或复选框. 每种控件有自己可识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件等等. 事件就是用户对窗口 ...

  7. Android View系列(二):事件分发机制源码解析

    概述 在介绍点击事件规则之前,我们需要知道我们分析的是MotionEvent,即点击事件,所谓的事件分发就是对MotionEvent事件的分发过程,即当一个MotionEvent生成以后,系统需要把这 ...

  8. android SDK-25事件分发机制--源码正确解析

    android SDK-25事件分发机制–源码正确解析 Android 事件分发分为View和ViewGroup的事件分发,ViewGroup比View过一个拦截判断,viewgroup可以拦截事件, ...

  9. Qt编写输入法源码V2019,未采用Qt系统层输入法框架,独创输入切换机制

    Qt编写输入法源码V2019 未采用Qt系统层输入法框架,独创输入切换机制. 纯QWidget编写,支持任何目标平台(亲测windows.linux.嵌入式linux等),支持任意Qt版本(亲测Qt4 ...

最新文章

  1. tf.keras.activations.relu 激活函数 示例
  2. HDU-3573 Buy Sticks
  3. python链表翻转_反转链表(两种Python解法)
  4. OpenSAP Fiori Elements 公开课第四单元视频的中文字幕
  5. thinkphp mysql desc table_数据库表结构_ThinkPHP 数据库表结构处理类(简单实用)-云栖社区-阿里云...
  6. Bootstrap 聚合
  7. LSTM TF核心实现代码笔记
  8. mysql 判断数据是否在_MySQL更新数据之前是否判断数据是否有被修
  9. 在网页中嵌入任意字体(特殊字体/自定义字体)的解决方案
  10. vscode+sftp 开发模式环境的同步
  11. window对象小结
  12. 通过RHCE认证之路
  13. xcap工具使用心得
  14. 【汉化】使用gettext和poedit对xibo-mo文件进行汉化编辑
  15. TPC,TPCC,TPMC(计算机性能衡量指标)
  16. 浙江师范大学c语言函数实验答案,C语言实验浙江师范大学8-2 D阵列+答案.doc
  17. c++模板函数的声明和定义该在什么文件里?
  18. 工作室培训第一周总结
  19. 强大的虚拟机软件vmware图文使用教程
  20. 基于c语言的语法分析器的实现

热门文章

  1. 手把手Fiddler掌握
  2. 四种常见的 POST 提交数据方式
  3. idea junit简单实践
  4. Glib实例学习(5)平衡二叉树
  5. Microsoft Visual C++ 14.0 is required
  6. python求解方程组
  7. LIME:一种解释机器学习模型的方法
  8. javascript语言学习
  9. eclipse中各种查找
  10. day02 文件读写