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

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

本文关键字:运行 是什么 机制 线程 对象 是否 单独 QTimer      更新时间:2023-10-16

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

例如,

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

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

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

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

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

事件是由OS异步传递的,这就是为什么还有其他事情正在发生。有但不在您的程序中。

这是否意味着timeout()是由OS处理的?是否有一些硬件可以跟踪时间并以适当的间隔发送中断?但是,如果是这种情况,那么许多计时器可以同时独立地运行,如何分别跟踪每个计时器?

什么是机制?

谢谢。

当我在QT 5中创建QTimer对象时,并使用start()开始启动它。 成员函数是创建一个单独的线程,可以跟踪 时间并定期调用超时()函数?

否;创建一个单独的线程将很昂贵,而且不是必需的,因此这不是实现QTimer的方式。

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

qtimer :: start()方法可以调用系统时间函数(例如getTimeofday()()或类似),以找出(在几毫秒内)调用start()的时间是什么时间。然后,它可以将十毫秒(或您指定的任何值)添加到那个时间,现在它具有记录,指示何时应该发射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()将返回,然后将执行响应该事件的代码,从而产生适当的操作,例如widgets更新更新或timeout(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对象,该对象说:"如果没有发生I/O事件(这是许多)微秒,那么您应该只是放弃并返回。"

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

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()。

btw如果该应用具有多个QTIMER同时活动,那没问题。在这种情况下,Sleepuntilthereissomethingtodo()只需要迭代所有活动的QTimers即可找到一个具有最小下一个时间邮票的QTIMER,并且仅使用最低时间戳来计算其最大时间间隔select()select()应该允许入睡。然后,在select()返回之后,dothingsthatneeddoingnow()也会在活动计时器上迭代,并仅针对那些下一个时间邮票不大于当前时间的人发出超时信号。事件环重复序列(尽快或尽可能慢),以提供多线程行为的外观,而无需实际需要多个线程。

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

要使QTimer工作,您的应用程序中必须有一个事件循环;也就是说,您必须在某个地方致电QCoreApplication::exec()。仅在事件循环运行时才进行计时事件。

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

在内部,QTimer只是在一定时间后使用QObject::startTimer方法来触发。这本身本身以某种方式告诉线程,它在时间之后要开火。

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

qtimer对象将自身寄存到EventDisPatcher(QabStractEventDisPatcher)中,该emplate nmand nefe to type qtimerevent type qtimerevent type qtimerevent type qtimerevent everts of EventDisPatcher(QabStractEventDisPatcher)每次有超时的特定注册QTimer时发送类型的事件。例如,在GNU/Linux上,有一个名为QeventDisPatcherunixPrivate的QabStractEventDispatcher的私有实现,该实现使计算当时考虑到平台API。Qtimerevent从qeventdispatcherunixprivate发送到qTimer对象所属的同一线程的事件循环的队列,即创建。

qeventDisPatcherunixPrivate由于某些操作系统事件或时钟而不会发射QTimerevent,而是因为它会定期检查Qtimer也居住的线程事件循环调用ProcessEvents时的超时。SE这里:https://code.woboq.org/qt5/qtbase/src/corelib/corelib/kernel/qeventdispatcher_unix.cpp.html#_zn27qeventdispatcherunixprivatec1ev

相关文章: