繁体   English   中英

QTimer对象是否在单独的线程中运行? 它的机制是什么?

[英]Does a QTimer object run in a separate thread? What is its mechanism?

当我在Qt 5中创建一个QTimer对象,并使用start()成员函数启动它时,是否创建了一个单独的线程来跟踪时间并定期调用timeout()函数?

例如,

QTimer *timer = new QTimer;
timer->start(10);
connect(timer,SIGNAL(timeout()),someObject,SLOT(someFunction()));

这里,程序如何知道timeout()何时发生? 我认为它必须在一个单独的线程中运行,因为我没有看到顺序程序如何跟踪时间并继续同时执行。 但是,我无法在Qt文档或其他任何地方找到有关此信息的任何信息以确认这一点。

我已经阅读了官方文档 ,而StackOverflow上的某些问题就像这样似乎非常相关,但我无法通过它们得到答案。

任何人都可以解释QTimer对象的工作机制吗?

在进一步搜索时,我发现根据比尔的 这个答案 ,我们提到了这一点

事件由操作系统异步传递,这就是看起来还有其他事情发生的原因。 有,但不是在你的程序中。

这是否意味着操作系统会处理timeout() 是否有一些硬件能够跟踪时间并以适当的间隔发送中断? 但如果是这种情况,由于许多计时器可以同时独立运行,每个计时器如何被单独跟踪?

机制是什么?

谢谢。

当我在Qt 5中创建一个QTimer对象,并使用start()成员函数启动它时,是否创建了一个单独的线程来跟踪时间并定期调用timeout()函数?

没有; 创建一个单独的线程将是昂贵的,并没有必要,所以这不是QTimer的实现方式。

这里,程序如何知道timeout()何时发生?

QTimer :: start()方法可以调用系统时间函数(例如gettimeofday()或类似函数)来查找(在几毫秒内)调用start()的时间。 然后,它可以添加10毫秒(或您指定的任何值),现在它有一条记录,指示何时应该发出timeout()信号。

因此,有了这些信息,它会做些什么来确保这一点?

要知道的关键事实是QTimer超时信号发射仅在Qt程序在Qt的事件循环内执行时才有效。 几乎每个Qt程序都会有这样的东西,通常在它的main()函数的底部附近:

QApplication app(argc, argv);
[...]
app.exec();

请注意,在典型的应用程序中,几乎所有应用程序的时间都将花在exec()调用中; 也就是说,app.exec()调用在应用程序退出之前不会返回

那么在你的程序运行时,exec()调用内部会发生什么? 有了像Qt这样的大型复杂库,它必然会变得复杂,但是说它正在运行一个概念上看起来像这样的事件循环并不是太简单:

 while(1)
 {
     SleepUntilThereIsSomethingToDo();  // not a real function name!
     DoTheThingsThatNeedDoingNow();     // this is also a name I made up
     if (timeToQuit) break;
 }

因此,当您的应用程序处于空闲状态时,该进程将在SleepUntilThereIsSomethingToDo()调用内部进入休眠状态,但只要事件到达需要处理(例如,用户移动鼠标,或按键,或数据到达套接字) ,或者等等),SleepUntilThereIsSomethingToDo()将返回,然后将执行响应该事件的代码,从而产生适当的操作,例如小部件更新或调用timeout()信号。

那么SleepUntilThereIsSomethingToDo()如何知道何时醒来并返回? 这将根据您运行的操作系统而有很大差异,因为不同的操作系统具有不同的API来处理此类事情,但实现此类功能的经典UNIX-y方式将是POSIX select()调用:

int select(int nfds, 
           fd_set *readfds, 
           fd_set *writefds,
           fd_set *exceptfds, 
           struct timeval *timeout);

请注意,select()采用三个不同的fd_set参数,每个参数都可以指定多个文件描述符; 通过将适当的fd_set对象传递给那些参数,可以使select()在您需要监视的一组文件描述符中的任何一个上唤醒I / O操作,以便程序可以处理I / O没有延迟。 然而,对我们来说有趣的部分是最终参数,这是一个超时参数。 特别是,您可以在这里传递一个struct timeval对象,它指向select():“如果在(这么多)微秒之后没有发生I / O事件,那么你应该放弃并返回”。

事实证明这非常有用,因为通过使用该参数,SleepUntilThereIsSomethingToDo()函数可以执行类似这样的操作(伪代码):

void SleepUntilThereIsSomethingToDo()
{
   struct timeval now = gettimeofday();  // get the current time
   struct timeval nextQTimerTime = [...];  // time at which we want to emit a timeout() signal, as was calculated earlier inside QTimer::start()
   struct timeval maxSleepTimeInterval = (nextQTimerTime-now);
   select([...], &maxSleepTimeInterval);  // sleep until the appointed time (or until I/O arrives, whichever comes first)
}

void DoTheThingsThatNeedDoingNow()
{
   // Is it time to emit the timeout() signal yet?
   struct timeval now = gettimeofday();
   if (now >= nextQTimerTime) emit timeout();

   [... do any other stuff that might need doing as well ...]
}   

希望这是有道理的,你可以看到事件循环如何使用select()的超时参数来允许它唤醒并在(大约)调用start时先前计算的时间发出timeout()信号( )。

顺便说一句,如果应用程序同时有多个QTimer激活,那没问题; 在这种情况下,SleepUntilThereIsSomethingToDo()只需迭代所有活动的QTimers以找到具有最小next-time-time戳的那个,并且仅使用该最小时间戳来计算select()的最大时间间隔应该被允许睡觉。 然后在select()返回后,DoTheThingsThatNeedDoingNow()也会迭代活动计时器,并仅为那些下一个超时时间戳不大于当前时间的人发出超时信号。 事件循环重复(根据需要快速或缓慢)以提供多线程行为的外观而不需要实际需要多个线程。

查看有关计时器文档以及QTimerQObject源代码,我们可以看到计时器正在分配给对象的线程/事件循环中运行。 来自doc:

要使QTimer工作,您必须在应用程序中有一个事件循环; 也就是说,你必须在某个地方调用QCoreApplication::exec() 定时器事件仅在事件循环运行时传送。

在多线程应用程序中,您可以在任何具有事件循环的线程中使用QTimer 要从非GUI线程启动事件循环,请使用QThread::exec() Qt使用计时器的线程亲和性来确定哪个线程将发出timeout()信号。 因此,您必须在其线程中启动和停止计时器; 无法从另一个线程启动计时器。

在内部, QTimer只使用QObject::startTimer方法在一定时间后触发。 这个本身以某种方式告诉它运行的线程在一段时间后开火。

因此,只要您不阻止事件队列,您的程序就可以持续良好地运行并跟踪计时器。 如果您担心计时器不是100%准确,请尝试将长时间运行的回调从其自己的线程中移出事件队列,或者为计时器使用不同的事件队列。

QTimer对象将自身注册到EventDispatcher(QAbstractEventDispatcher)中,每当特定的已注册QTimer超时时,它就会小心地发送类型为QTimerEvent的事件。 例如,在GNU / Linux上有一个名为QEventDispatcherUNIXPrivate的QAbstractEventDispatcher的私有实现,它使计算考虑到平台api。 QTimerEvent从QEventDispatcherUNIXPrivate发送到QTimer对象所属的同一线程的事件循环的队列中,即已创建。

由于某些OS系统事件或时钟,QEventDispatcherUNIXPrivate不会触发QTimerEvent,但是因为它定期检查当QTimer存在的线程事件循环调用processEvents时的超时。 请点击这里: https//code.woboq.org/qt5/qtbase/src/corelib/kernel/qeventdispatcher_unix.cpp.html#_ZN27QEventDispatcherUNIXPrivateC1Ev

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM