Страницы

Поиск по вопросам

суббота, 14 декабря 2019 г.

Механизм работы таймера в qt

#qt


Всем привет!
Может кто-то пояснить механизм работы таймера qt. Он формирует сообщение в очереди
сообщений или влияет на процесс обработки этой очереди?

Например:
Допустим у нас есть функция которая создает сигнал и кладет его в очередь потока.
Это вызывает слот потока в котором мы вызываем ту же функцию. 
По идее получается как только мы закончили отрабатывать слот мы выходим в очередь
обработки, там видим новый сигнал, и опять заходим в слот. 

Теперь если в том же потоке есть таймер. Он будет параллельно со слотом класть в
очередь сообщения о таймауте, как бы из другого потока? Сможет ли он прервать мой вечный
цикл указанный выше? Или он просто ставит где-то в обработчики очереди сообщений флажочек
и в нужное время вызывается сигнал таймера?

UPD: Спасибо за оба ответа, отдал предпочтение второму ответу, так как он все же
более полный и ближе, как мне показалось к тому что спрашивал. 

Резюмирую понимание устройства таймера: Сигнал таймаута рождается в момент возврата
к разбору очереди сообщения, именно там добываются необходимые сообщения от операционной
среды и определяется что таймер свое отработал. Поэтому без возврата к разбору очереди
событий таймера не появиться, ровно как их невозможно заблокировать если есть возвраты
к очереди сообщений. Важно что возврат формирует именно рождение сигнала, а не только
его обработку, до возврата сигнал не родится.  
    


Ответы

Ответ 1



Немного издали... Если сигнал подключается к слоту с флагом Qt::AutoConnection (используется по умолчанию) или Qt::DirectConnection, и вызываемый слот принадлежит объекту, находящемуся в том же потоке, что и объект, от которого исходит сигнал, то никакой обработчик очереди событий не вызывается. Производится вызов слота прямо на месте. Если этот слот посредством прямого подключения через сигнал будет вызывать сам себя, то случится бесконечная рекурсия, вплоть до переполнения стека, ну и конечно неизбежного краха приложения. Для того, чтобы связка сигнал-слот срабатывала через очередь в рамках одного и того же потока необходимо всегда явно указывать флаг Qt::QueuedConnection. Только в этом случае создаётся соответствующий наследник класса события, который и помещается в очередь событий. Внутренняя реализация QTimer пользуется методами регистрации таймеров операционной системы, на которой выполняется приложение (это позволяет указывать таймеру дополнительные флаги, в случае, если требуется, например, повышенная точность). Соответственно, именно она формирует события о том, что время такого-то таймера в очередной раз истекло. Точно такие же события поступают при движении курсора мыши и нажатии клавиш. Эти нативные события попадают в очередь событий приложения одно за другим. Все события обрабатываются строго по очереди. Здесь как никогда верно правило: "Кто первый встал, того и тапки". Это означает, что если таймер создан с точностью, допускающей определённую погрешность в интервале, то события таймера будут поступать на обработку с непредсказуемым (в рамках погрешности, разумеется) интервалом и могут попасть в очередь до или после любого другого события. Если объект таймера существует в отдельном потоке, у которого имеется своя собственная очередь событий, и при этом будет пытаться вызывать слот объекта, находящегося в другом потоке, то событие о вызове слота попадёт в очередь событий целевого потока. В этом случае будет действовать ровно то же самое правило про тапки: если перед поступившим событием уже имеются какие-то события, то будут выполнены сначала они, а событие таймера на вызов слота подождёт своей очереди. Однако существуют подводные камни, возможность появления которых стоит иметь в виду. Предположим, имеется следующая схема: Событие А: слот СЛОТ вызывает сам себя через очередь событий; Событие Б: таймер вызывает СЛОТ с неким интервалом; Получится следующее: Вызовы А будут идти друг за другом, как и положено. Поскольку каждое новое событие всегда встаёт вконец очереди, то вызов Б через какое-то время без проблем вклинится в порядок вызовов А. Однако каждый вызов слота СЛОТ всегда порождает новый вызов А. Таким образом, после каждого Б будет порождаться не один новый А, а второй, третий (и т.д.) бесконечные циклы из А.

Ответ 2



TL; DR - Да, таймер сможет вклиниться в косвенную рекурсию, но так делать не стоит Для создания "вечных циклов" в обработке сообщений есть специальный механизм: As a special case, a QTimer with a timeout of 0 will time out as soon as all the events in the window system's event queue have been processed. This can be used to do heavy work while providing a snappy user interface: Особый случай - QTimer с таймаутом 0, который срабатывает как только очередь сообщений оказывается пустой. Это позволяет выполнять вычисления и одновременно показывать пользователю отзывчивый интерфейс. Это косвенно говорит о том, что таймер работает как описанный вами "флажочек", и проверка на сработку таймера происходит всякий раз, когда программа оказывается в очереди сообщений. При этом, можно создать и отдельный поток, и в нем создать таймер. Сообщения этого таймера будут видеть все подписавшиеся, но вот управлять им можно будет только из того потока, в котором он создан. Источник - мануал

Комментариев нет:

Отправить комментарий