gpt4 book ai didi

c++ - QTimer对象是否在单独的线程中运行?其作用机理是什么?

转载 作者:IT老高 更新时间:2023-10-28 13:00:25 35 4
gpt4 key购买 nike

When I create a QTimer object in Qt 5, and start it using the start() member function, is a separate thread created that keeps track of the time and calls the timeout() function at regular intervals?



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

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

我已经阅读了 the official documentation,关于StackOverflow的某些问题(例如 thisthis)似乎很相关,但是我无法通过它们得到答案。

谁能解释 QTimer对象工作的机制?

在进一步搜索时,我发现按照 this answerBill所述,

Events are delivered asynchronously by the OS, which is why it appears that there's something else going on. There is, but not in your program.



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

机制是什么?

谢谢你。

最佳答案

When I create a QTimer object in Qt 5, and start it using the start() member function, is a separate thread created that keeps track of the time and calls the timeout() function at regular intervals?



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

Here, how does the program know when timeout() occurs?



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

因此,有了这些信息,它将如何确保这种情况发生?

要知道的关键事实是,仅当/当您的Qt程序在Qt的事件循环内执行时,QTimer超时信号发射才起作用。几乎每个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没有延迟。但是,对我们来说有趣的部分是最后一个参数,它是一个超时参数。特别是,您可以在此处传递一个对select()说的 struct timeval对象:“如果在(这么多)微秒后未发生任何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()的timeout参数来使其唤醒,并在(大约)调用start()之前计算出的时间发出timeout()信号。 )。

顺便说一句,如果该应用程序同时激活了多个QTimer,那没问题;在那种情况下,SleepUntilThereIsSomethingToDo()只需遍历所有 Activity 的QTimers即可找到具有最小下次超时时间戳的QTimer,并仅使用该最小时间戳来计算select()的最大时间间隔。应该被允许 sleep 。然后,在select()返回之后,DoTheThingsThatNeedDoingNow()也对 Activity 计时器进行迭代,并且仅针对其下一个超时时间戳不大于当前时间的计时器发出超时信号。重复事件循环(根据需要快速或慢速进行),以产生类似的多线程行为,而实际上并不需要多个线程。

关于c++ - QTimer对象是否在单独的线程中运行?其作用机理是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42279360/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com