QTimer对象是否在单独的线程中运行?其作用机理是什么? [英] Does a QTimer object run in a separate thread? What is its mechanism?

查看:186
本文介绍了QTimer对象是否在单独的线程中运行?其作用机理是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我在Qt 5中创建一个QTimer对象并使用start()成员函数启动它时,是否创建了一个单独的线程来跟踪时间并定期调用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 *timer = new QTimer;
timer->start(10);
connect(timer,SIGNAL(timeout()),someObject,SLOT(someFunction()));

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

Here, how does the program know when timeout() occurs? I think it would have to run in a separate thread, as I don't see how a sequential program could keep track of the time and continue its execution simultaneously. However, I have been unable to find any information regarding this either in the Qt documentation or anywhere else to confirm this.

我已阅读官方文档,以及有关StackOverflow的某些问题,例如似乎很相关,但是我无法通过它们得到答案.

I have read the official documentation, and certain questions on StackOverflow such as this and this seem very related, but I could not get my answer through them.

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

Could anyone explain the mechanism through which a QTimer object works?

进一步搜索时,我发现此答案显示了这一点. com/users/102/bill>法案,其中提到

On searching further, I found that as per this answer by Bill, it is mentioned that

事件由OS异步传递,这就是为什么似乎还有其他事情发生的原因.有,但是没有在您的程序中.

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

Does it mean that timeout() is handled by the OS? Is there some hardware that keeps track of the time and send interrupts at appropriate intervals? But if this is the case, as many timers can run simultaneously and independently, how can each timer be separately tracked?

是什么机制?

谢谢.

推荐答案

当我在Qt 5中创建QTimer对象并使用start()启动它时 成员函数,是创建的一个单独的线程,用于跟踪 时间并定期调用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的实现方式.

No; creating a separate thread would be expensive and it isn't necessary, so that isn't how QTimer is implemented.

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

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

QTimer :: start()方法可以调用系统时间函数(例如

The QTimer::start() method can call a system time function (e.g. gettimeofday() or similar) to find out (to within a few milliseconds) what the time was that start() was called. It can then add ten milliseconds (or whatever value you specified) to that time and now it has a record indicating when the timeout() signal is supposed to be emitted next.

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

So having that information, what does it then do to make sure that happens?

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

The key fact to know is that QTimer timeout-signal-emission only works if/when your Qt program is executing inside Qt's event loop. Just about every Qt program will have something like this, usually near the bottom its main() function:

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

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

Note that in a typical application, almost all of the application's time will be spent inside that exec() call; that is to say, the app.exec() call will not return until it's time for the application to exit.

那么在您的程序运行时,exec()调用内部发生了什么?对于像Qt这样的大型复杂库,它一定很复杂,但是要说它正在运行一个事件循环,从概念上来说,看起来并没有什么简化:

So what is going on inside that exec() call while your program is running? With a big complex library like Qt it's necessarily complicated, but it's not too much of a simplification to say that it's running an event loop that looks conceptually something like this:

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

因此,当您的应用程序处于空闲状态时,该过程将在SleepUntilThereIsSomethingToDo()调用中进入睡眠状态,但是一旦到达需要处理的事件(例如,用户移动鼠标或按下键,或数据到达)在套接字等上),SleepUntilThereIsSomethingToDo()将返回,然后将执行响应该事件的代码,从而导致相应的操作,例如小部件更新或调用timeout()信号.

So when your app is idle, the process will be put to sleep inside the SleepUntilThereIsSomethingToDo() call, but as soon as an event arrives that needs handling (e.g. the user moves the mouse, or presses a key, or data arrives on a socket, or etc), SleepUntilThereIsSomethingToDo() will return and then the code to respond to that event will be executed, resulting in the appropriate action such as the widgets updating or the timeout() signal being called.

那么SleepUntilThereIsSomethingToDo()如何知道何时该醒来并返回?这将取决于您所运行的操作系统而有很大不同,因为不同的操作系统具有不同的API来处理此类事情,但是实现此类功能的经典UNIX-y方法是使用POSIX

So how does SleepUntilThereIsSomethingToDo() know when it is time to wake up and return? This will vary greatly depending on what OS you are running on, since different OS's have different APIs for handling this sort of thing, but a classic UNIX-y way to implement such a function would be with the POSIX select() call:

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事件,那么您应该只放弃并返回".

Note that select() takes three different fd_set arguments, each of which can specify a number of file descriptors; by passing in the appropriate fd_set objects to those arguments you can cause select() to wake up the instant an I/O operations becomes possible on any one of a set of file descriptors you care to monitor, so that your program can then handle the I/O without delay. However, the interesting part for us is the final argument, which is a timeout-argument. In particular, you can pass in a struct timeval object here that says to select(): "If no I/O events have occurred after (this many) microseconds, then you should just give up and return anyway".

这非常有用,因为通过使用该参数,SleepUntilThereIsSomethingToDo()函数可以执行以下操作(伪代码):

That turns out to be very useful, because by using that parameter, the SleepUntilThereIsSomethingToDo() function can do something like this (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()的timeout参数来使其唤醒并在(大约)它先前计算时的时间发出timeout()信号.叫做start().

Hopefully that makes sense, and you can see how the event loop uses select()'s timeout argument to allow it to wake up and emit the timeout() signal at (approximately) the time that it had previously calculated when you called start().

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

Btw if the app has more than one QTimer active simultaneously, that's no problem; in that case, SleepUntilThereIsSomethingToDo() just needs to iterate over all of the active QTimers to find the one with the smallest next-timeout-time stamp, and use only that minimum timestamp for its calculation of the maximum time-interval that select() should be allowed to sleep for. Then after select() returns, DoTheThingsThatNeedDoingNow() also iterates over the active timers and emits a timeout signal only for those whose next-timeout-time stamp is not greater than the current time. The event-loop repeats (as quickly or as slowly as necessary) to give a semblance of multithreaded behavior without actually requiring multiple threads.

这篇关于QTimer对象是否在单独的线程中运行?其作用机理是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆