接上一篇文章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 < 32 if (qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB")) { static QBasicMutex mutex; QMutexLocker locker(&mutex); if (!g_thread_supported()) g_thread_init(NULL); } #endif if (mainContext) {//根据mainContext是否为空创建新的mainContext g_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 source postEventSource = 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 socketNotifierSource socketNotifierSource = 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 sources timerSource = 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 notifier static 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 source if (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 source return 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
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出