当我在 Qt 5 中创建
QTimer
对象,并使用start()
成员函数启动它时,创建了一个单独的线程来跟踪时间并调用timeout()
定期运行?
例如,
QTimer *timer = new QTimer;
timer->start(10);
connect(timer,SIGNAL(timeout()),someObject,SLOT(someFunction()));
在这里,程序如何知道 timeout()
何时发生?我认为它必须在单独的线程中运行,因为我看不到顺序程序如何跟踪时间并同时继续执行。但是,我无法在 Qt 文档或其他任何地方找到有关此的任何信息来确认这一点。
我已经阅读 了官方文档,并且 StackOverflow 上的某些问题,例如 this 和 this 似乎非常相关,但我无法通过它们得到答案。
谁能解释 QTimer
对象工作的机制?
事件由操作系统异步传递,这就是为什么看起来还有其他事情发生的原因。有,但不在您的程序中。
这是否意味着 timeout()
由操作系统处理?是否有一些硬件可以跟踪时间并以适当的间隔发送中断?但是如果是这样的话,既然很多定时器可以同时独立运行,那么如何分别跟踪每个定时器呢?
机制是什么?
谢谢你。
原文由 GoodDeeds 发布,翻译遵循 CC BY-SA 4.0 许可协议
不;创建一个单独的线程会很昂贵而且没有必要,所以这不是 QTimer 的实现方式。
QTimer::start() 方法可以调用系统时间函数(例如 gettimeofday() 或类似函数)来找出(在几毫秒内)调用 start() 的时间。然后它可以将 10 毫秒(或您指定的任何值)添加到该时间,现在它有一条记录指示应该何时发出 timeout() 信号。
那么有了这些信息,它会做什么来确保发生这种情况?
要知道的关键事实是,QTimer timeout-signal-emission 仅在您的 Qt 程序在 Qt 的事件循环内执行时才有效。几乎每个 Qt 程序都会有这样的东西,通常在其 main() 函数的底部附近:
请注意,在典型的应用程序中,几乎所有应用程序的时间都将花在该 exec() 调用中;也就是说,在应用程序退出之前,app.exec() 调用 _不会返回_。
那么当你的程序运行时,exec() 调用内部发生了什么?对于像 Qt 这样的大型复杂库,它一定很复杂,但是说它正在运行一个在概念上看起来像这样的事件循环并不过分:
因此,当您的应用程序空闲时,进程将在 SleepUntilThereIsSomethingToDo() 调用中进入睡眠状态,但是一旦需要处理的事件到达(例如,用户移动鼠标,或按下键,或数据到达套接字等),SleepUntilThereIsSomethingToDo() 将返回,然后响应该事件的代码将被执行,从而导致适当的操作,例如小部件更新或调用 timeout() 信号。
那么 SleepUntilThereIsSomethingToDo() 是如何知道何时该醒来并返回的呢?这将根据您运行的操作系统而有很大差异,因为不同的操作系统具有不同的 API 来处理此类事情,但实现此类功能的经典 UNIX-y 方法是使用 POSIX select() 调用:
注意 select() 接受三个不同的 fd_set 参数,每个参数可以指定多个文件描述符;通过将适当的 fd_set 对象传递给这些参数,您可以使 select() 立即唤醒您要监视的一组文件描述符中的任何一个 I/O 操作,以便您的程序可以处理I/O 无延迟。然而,对我们来说有趣的部分是最后一个参数,它是一个超时参数。特别是,您可以在此处传入一个
struct timeval
对象,该对象对 select() 说:“如果在(这么多)微秒后没有发生 I/O 事件,那么您应该放弃并返回” .事实证明这非常有用,因为通过使用该参数,SleepUntilThereIsSomethingToDo() 函数可以执行以下操作(伪代码):
希望这是有道理的,并且您可以看到事件循环如何使用 select() 的 timeout 参数来允许它唤醒并在(大约)它之前计算的时间发出 timeout() 信号调用 start( )。
顺便说一句,如果应用程序同时有多个 QTimer 处于活动状态,那没问题;在这种情况下,SleepUntilThereIsSomethingToDo() 只需要遍历所有活动的 QTimer 以找到具有最小下一次超时时间戳的那个,并仅使用该最小时间戳来计算 select() 的最大时间间隔应该允许睡觉了。然后在 select() 返回后,DoTheThingsThatNeedDoingNow() 还会遍历活动计时器,并仅针对那些下一个超时时间戳不大于当前时间的计时器发出超时信号。事件循环重复(根据需要快速或慢速)以提供多线程行为的外观,而实际上不需要多个线程。