Выполняется ли объект QTimer в отдельном потоке? Каков его механизм?

когда я создаю QTimer объект в Qt 5 и запустите его с помощью start() функция-член, это отдельный поток, созданный, который отслеживает время и вызывает timeout() функции через регулярные промежутки времени?

например,

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

здесь, как программа знает, когда timeout() происходит? Я думаю, что он должен работать в отдельном потоке, поскольку я не вижу, как последовательная программа может отслеживать время и продолжать его выполнение одновременно. Однако я не смог найти никакой информации об этом ни в документации Qt, ни где-либо еще, чтобы подтвердить это.

я прочитала официальная документация, и некоторые вопросы по StackOverflow, такие как этой и этой кажутся очень связанными, но я не мог получить свой ответ через них.

может ли кто-нибудь объяснить механизм, через который a QTimer объект работает?

On продолжая поиск, я обнаружил, что в соответствии с ответ by Билл, указано, что

события доставляются ОС асинхронно, поэтому кажется, что происходит что-то еще. Есть, но не в вашей программе.

значит ли это, что timeout() обрабатывается ОС? Есть ли какое-то оборудование, которое отслеживает время и отправляет прерывания через соответствующие промежутки времени? Но если это так, то как много таймеры могут работать одновременно и независимо, как можно отдельно отслеживать каждый таймер?

Что такое механизм?

спасибо.

2 ответов


когда я создаю объект QTimer в Qt 5 и запускаю его с помощью start() функция-член-это отдельный поток, созданный для отслеживания время и вызовы функции timeout () через регулярные промежутки времени?

нет; создание отдельного потока было бы дорого, и это не обязательно, так что QTimer не реализован.

здесь, как программа знает, когда происходит timeout ()?

QTimer:: старт() метод может вызывать функцию системного времени (например,gettimeofday() или аналогичный), чтобы узнать (в течение нескольких миллисекунд), какое время было вызвано start (). Затем он может добавить десять миллисекунд (или любое указанное вами значение) к этому времени, и теперь у него есть запись, указывающая, когда сигнал timeout () должен быть испущен следующим.

Итак, имея эту информацию, что он делает, чтобы убедиться, что это произойдет?

ключевой факт, котор нужно знать что QTimer timeout-signal-emission работает только в том случае, если / когда ваша программа Qt выполняется внутри цикла событий Qt. Почти каждая программа Qt будет иметь что-то вроде этого, обычно внизу ее основная() функция:

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

обратите внимание, что в типичном приложении почти все время приложения будет потрачено внутри этого вызова exec (); то есть приложения.exec () call не вернется пока не пришло время для выхода приложения.

так что же происходит внутри этот вызов exec () во время работы вашей программы? С большой сложной библиотекой, такой как Qt, это обязательно сложно, но это не слишком упрощение, чтобы сказать, что он запускает цикл событий, который концептуально выглядит примерно так:

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

поэтому, когда ваше приложение простаивает, процесс будет помещен в спящий режим внутри вызова SleepUntilThereIsSomethingToDo (), но как только приходит событие, которое требует обработки (например, пользователь перемещает мышь или нажимает клавишу, или данные поступают на socket или etc), 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() проснуться в тот момент, когда операции ввода-вывода становятся возможными для любого из набора файловых дескрипторов, которые вы хотите контролировать, чтобы ваша программа могла обрабатывать ввод-вывод без задержки. Однако интересной частью для нас является последний аргумент, который является аргументом тайм-аута. В частности, вы можете пройти в struct timeval объект здесь, который говорит, чтобы выбрать ():"Если никакие события ввода-вывода не произошли после (это много) микросекунд, то вы должны просто сдаться и вернуться в любом случае".

это оказывается очень полезным, потому что, используя этот параметр, функция 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().

Btw если приложение имеет более одного активного QTimer одновременно, это не проблема; в этом случае SleepUntilThereIsSomethingToDo() просто нужно перебрать все активные QTimers, чтобы найти тот, у которого наименьший штамп следующего таймаута, и использовать только это минимальная временная метка для расчета максимального интервала времени, для которого select() должен быть разрешен сон. Затем, после возврата select (), DoTheThingsThatNeedDoingNow () также повторяет активные таймеры и выдает сигнал тайм-аута только для тех, чья метка следующего тайм-аута не больше текущего времени. Цикл событий повторяется (так быстро или так медленно, как это необходимо), чтобы дать видимость многопоточного поведения, фактически не требуя нескольких потоков.


смотреть на!--10-->документация о таймерах и код QTimer и QObject мы видим, что таймер работает в цикле потока / события, который назначен объекту. От Дока:

на QTimer чтобы работать, вы должны иметь цикл событий в своем приложении; то есть вы должны вызвать QCoreApplication::exec() куда-то. События таймера будут доставляться только во время выполнения цикла событий.

в многопоточных приложения, вы можете использовать QTimer в любом потоке, который имеет цикл событий. Чтобы запустить цикл событий из потока без GUI, используйте QThread::exec(). Qt использует сродство потоков таймера, чтобы определить, какой поток будет испускатьtimeout() сигнал. Из-за этого вы должны запустить и остановить таймер в его потоке; невозможно запустить таймер из другого потока.

внутри QTimer просто использует QObject::startTimer метод для После определенного количества времени. Этот сам как-то говорит нить, на которой он работает, срабатывает через некоторое время.

таким образом, ваша программа отлично работает непрерывно и отслеживает таймеры, пока вы не блокируете очередь событий. Если вы беспокоитесь о том, что ваш таймер не на 100% точен, попробуйте переместить длительные обратные вызовы из очереди событий в свой собственный поток или использовать другую очередь событий для таймеров.