Russian Qt Forum

Qt => Общие вопросы => Тема начата: Akon от Сентябрь 01, 2011, 16:51



Название: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Akon от Сентябрь 01, 2011, 16:51
сабж


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: ритт от Сентябрь 01, 2011, 19:38
рассоединить и соединить с Qt::DirectConnection ?
а что за юзкейс, позвольте полюбопытсвовать?


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Akon от Сентябрь 01, 2011, 19:58
Из потока прут данные с уведомление по сигналу. Обработка производится синхронно (Qt::DirectConnection) пользовательским объектом, "живущим" в другом потоке. Если опустить параметр соединения, то для данного случая получится Qt::QueudConnection. Задача: предупредить  пользователя (выдать диагностическое сообщение), если он подключится без Qt::DirectConnection (т.е. явно его не укажет).


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: LisandreL от Сентябрь 01, 2011, 20:31
В слоте сравнить sender()->thread() c QThread::currentThread(), на всякий случай проверив sender() на 0.


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Akon от Сентябрь 01, 2011, 21:05
sender()->thread() - дает поток, в котором объект "живет"; а сигнал объекта может быть испущен из любого другого потока, и sender()->thread() будет тем же самым.


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: ритт от Сентябрь 01, 2011, 22:17
ну, вот и правильно. как Вы выше указали, sender() в днном случае - это поток, из которого "прут данные".
если sender()->thread() == QThread::currentThread() /* равносильно qobject_cast<QThread *>(sender()) == QThread::currentThread() */, значит Qt::DirectConnection; иначе ругаемся

но всё-равно этот юз-кейс какой-то странный...ни разу за свою практику мне не приходилось делать подобных проверок.
и, кстати, в случае неосмотрительности при синхронизации потоков рискуете получить по ушам) я бы советовал использовать здесь Qt::QueuedBlockedConnection /* либо пересмотреть архитектуру приложения */


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: LisandreL от Сентябрь 01, 2011, 22:32
равносильно qobject_cast<QThread *>(sender()) == QThread::currentThread()
Щито?
sender() - это объект испустивший сигнал, он не обязан наследоваться от QThread.
Более того даже у потоков this != this->thread(), за исключением случая использования moveToThread( this ).


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: ритт от Сентябрь 02, 2011, 01:30
если наследник QThread испускает сигнал, то равносильно. про moveToThread( this ) всё верно.


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Akon от Сентябрь 02, 2011, 14:04
Цитировать
ну, вот и правильно. как Вы выше указали, sender() в днном случае - это поток, из которого "прут данные".
если sender()->thread() == QThread::currentThread() /* равносильно qobject_cast<QThread *>(sender()) == QThread::currentThread() */, значит Qt::DirectConnection; иначе ругаемся

Равносильно, если наследник QThread испускает сигнал, и для наследника сделано moveToThread( this ).

Пусть источник и приемник сигнала живут в одном потоке. Источник не QThread. В источнике из отдельного внутреннего потока выбрасывается сигнал: Q_EMIT <источник>.someSignal(...).
В слоте при Qt::DirectConnection будет sender()->thread() != QThread::currentThread(), при других типах соединения будет равенство. Но это частный случай: источник и приемник сигнала живут в одном потоке.

Цитировать
но всё-равно этот юз-кейс какой-то странный...ни разу за свою практику мне не приходилось делать подобных проверок.
и, кстати, в случае неосмотрительности при синхронизации потоков рискуете получить по ушам) я бы советовал использовать здесь Qt::QueuedBlockedConnection /* либо пересмотреть архитектуру приложения */

В ситуациях большого объема исходных передаваемых данных, часто использую подобный дизайн в силу его простоты и производительности. После синхронной обработки объем вторичных данных может быть значительно уменьшен, и тут уже к месту Qt::QueuedConnection.

С Qt::QueuedConnection - издержки на передачу данных между потоками (копирование), или издержки на создание и поддержание общей очереди данных. Источник, который производит данные в отдельном потоке, не нагружается заботой о том, как его данные будут обрабатывать: синхронно или асинхронно.

Qt::QueuedBlockedConnection - зачастую (практически всегда, когда ждем гуй) убийственная блокировка для источников реального времени.



Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: ритт от Сентябрь 03, 2011, 03:39
Qt::QueuedBlockedConnection - зачастую (практически всегда, когда ждем гуй) убийственная блокировка для источников реального времени.

если не считать затрат на копирование данных, то более убийственная, чем Qt::DirectConnection между потоками?)


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Akon от Сентябрь 03, 2011, 06:59
Простите, не понял ваш пост.


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: ритт от Сентябрь 03, 2011, 11:53
С Qt::QueuedConnection - издержки на передачу данных между потоками (копирование), или издержки на создание и поддержание общей очереди данных. Источник, который производит данные в отдельном потоке, не нагружается заботой о том, как его данные будут обрабатывать: синхронно или асинхронно.

в силу implicit sharing природы какого-нибудь QByteArray издержки на копирование оного минимальны и можно условно принять за 0.
очередь данных уже создана и успешно поддерживается за Вас - если не производить никакого колдовства и придерживаться схемы "один обработчик не более чем для одного источника", то последовательность вызовов слота для данных будет совпадать с последовательностью эмитов сигнала с теми же данными. можете убедиться, написав несложный тест.
плюсы очевидны - поток источника данных не занимается чужой работой (а именно, обработкой данных, которая будет выполняться "клиенстким" объектом в своём потоке); и не нужно беспокоиться о межпоточной синхронизации.

Qt::QueuedBlockedConnection - зачастую (практически всегда, когда ждем гуй) убийственная блокировка для источников реального времени.
> если не считать затрат на копирование данных, то более убийственная, чем Qt::DirectConnection между потоками?)

перефразирую: в чём принципиальное отличие Qt::DirectConnection от Qt::QueuedBlockedConnection в задаче, аналогичной Вашей? в чём заключается "убийственная блокировка"?


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Akon от Сентябрь 03, 2011, 13:28
Цитировать
в силу implicit sharing природы какого-нибудь QByteArray издержки на копирование оного минимальны и можно условно принять за 0.
очередь данных уже создана и успешно поддерживается за Вас - если не производить никакого колдовства и придерживаться схемы "один обработчик не более чем для одного источника", то последовательность вызовов слота для данных будет совпадать с последовательностью эмитов сигнала с теми же данными. можете убедиться, написав несложный тест.
плюсы очевидны - поток источника данных не занимается чужой работой (а именно, обработкой данных, которая будет выполняться "клиенстким" объектом в своём потоке); и не нужно беспокоиться о межпоточной синхронизации.

Да, передавать данные посредством implicit sharing или посредством shared pointer очень хороший вариант. При отсутствии специфичных для моей задачи ограничений остановился бы на нем.

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

Теперь рассмотрим клиента, который вычисляет мощность по куску сигнала (т.е. просто получает число) и обновляет прогрессбар. Оптимальным вариантом для клиента будет DirectConnection, синхронное вычисление значения и обновление проггрессбара по QueudConnection.

"убийственная блокировка" от Qt::QueuedBlockedConnection будет в том, что внутренний поток остановится, пока гуй не доберется до очереди сообщений и не вызовет слот. Это чревато банальным пропуском записываемых данных.


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: LisandreL от Сентябрь 03, 2011, 13:46
"убийственная блокировка" от Qt::QueuedBlockedConnection будет в том, что внутренний поток остановится, пока гуй не доберется до очереди сообщений и не вызовет слот. Это чревато банальным пропуском записываемых данных.
Qt::DirectConnection и длинный код слота также остановят ваш поток.


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Akon от Сентябрь 03, 2011, 14:13
Да, но это локальный участок, и пользователь должен не допускать этого. Ведь только ему известна "тяжесть" обработки. Так все коллбэки и строятся. Если обработка достаточно "тяжела", то пользователь должен вынести ее из слота и сделать асинхронной. Qt::DirectConnection в данном случае своего рода общий знаменатель.


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: ритт от Сентябрь 03, 2011, 15:42
Вы, вроде бы, механизм понимаете правильно, но жонглиреуете фактами, как заправский циркач, игнорируя плюсы и выдавая минусы за плюсы другого характера :)
Qt::DirectConnection или Qt::QueuedBlockedConnection - в Вашем случае разницы никакой (я намеренно опускаю наличие межпоточной блокировки во втором случае). никакого пропуска данных там нет и не будет. упрощая описанную в третьем посте схему, приходим к тому, что слот Вам вовсе и не нужен - достаточно приватного метода, исполняемого в контексте источника данных. но т.к. у Вас некий "клиентский" обект, то допускаю, что какие-то его члены должны быть потоко-безопасными (а иначе я вообще не вижу смысла в этом объекте). подключая слот через Qt::DirectConnection, Вы усложняете себе жизнь необходимостью скрупулёзно следить за lock-free потокобезопасностью, но получаете возможность использовать некий дефицитный ресурс в качестве буфера данных. независимо от того, "тяжёлая" операция производится в слоте или нет, источник данных (поток) занимается не своей задачей, данные в это время накапливаются в некотором ином буфере, возникает потенциальная задержка, а возможности как-либо контролировать её нет (разве что, "пробрасывать" часть данных - чего Вы, кажется, пытаетесь избежать).
давайте рассмотрим простейшую альтернативу: источник данных имеет пишет в не-такой-дефицитный implicitly shared буфер и выбрасывает queued сигнал со ссылкой на него { QMetaObject::invokeMethod(this, "dataChunkReceived", Qt::QueuedConnection, Q_ARG(QByteArray, &ba)); }, абсолютно не беспокоясь о том, кто и с какими параметрами подключён к сигналу dataChunkReceived(const QByteArray &), после чего сразу же возвращается к своей прямой обязанности - ждать данные и складывать их в буфер. имея пару-тройку буферов в распоряжении, вероятность memory reallocation любого из буферов стремится к нулю. слот, подключённый к вышеобозначенному сигналу, уже в другом контексте выполняет над данными произвольные операции, швыряется произвольными сигналами либо же попросту спит минуту, но следующий кусок данных он таки получит. проблема только в том, что если объект действительно спит минуту, в очереди событий будут накапливаться всё новые и новые данные, что эффективно решает "проброской" данных, которые уже неактуальны. но раз уж у Вас достаточно навыков и времени на поддержание lock free синхронизации между несколькими объектами, то задача обработки данных "в срок" и/или "проброски" неактуальных данных для Вас будет проще пареной репы;) если же "проброска" данных исключена, несложно добавить в источник данных механизм обнаружения неэффективного использования буферов и усыпления потока - будет 1-в-1 то же самое, что у Вас сейчас происходит при вызове слота, подключённого с Qt::DirectConnection.

у нас подобная схема успешно справляется в десятками видео-потоков средней плотности. включая рендеринг. не побоюсь предположить, что в контексте Вашей задачи это были бы сотни потоков аудио стандартного битрейта (если не упрётся в ограничения звуковой карты).

но я не навязываюсь с данной схемой - просто рассмотрел возможные альтернативы :)


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Igors от Сентябрь 03, 2011, 20:21
Ну почему "жонглирует", case вполне естественный, напр клиенту нужно быстро проверить "а нужны ли мне пришедшие данные" (может и нет). Добраться до нитки клиента может быть очень непросто, поэтому выполнение в нитке вызывающего. Делать это на стороне сервера - открывать подробности реализации клиента, не гуд.

Как определить - ну а если незатейливо в run клиента запомнить его currentThreadId и проверять на неравенство текущему?


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Akon от Сентябрь 03, 2011, 23:44
но я не навязываюсь с данной схемой - просто рассмотрел возможные альтернативы :)

Всегда рад различного рода альтернативам  :) Спасибо.

Цитировать
давайте рассмотрим простейшую альтернативу: источник данных имеет пишет в не-такой-дефицитный implicitly shared буфер и выбрасывает queued сигнал со ссылкой на него { QMetaObject::invokeMethod(this, "dataChunkReceived", Qt::QueuedConnection, Q_ARG(QByteArray, &ba)); }, абсолютно не беспокоясь о том, кто и с какими параметрами подключён к сигналу dataChunkReceived(const QByteArray &), после чего сразу же возвращается к своей прямой обязанности - ждать данные и складывать их в буфер. имея пару-тройку буферов в распоряжении, вероятность memory reallocation любого из буферов стремится к нулю

Спору нет - такая схема очень элегантна, когда источнику данных позволительно выделить дополнительную память, и частота такого выделения приемлемо низка. Схему можно еще немного оптимизировать, если под чанки использовать implicitly shared но без copy on write (т.е. просто allocate on write).

Пусть в приведенном мною примере с вычислением мощности чанка происходит переполнение буфера - в вашей схеме будет выделена память под целый чанк, в моей, этого не будет, поскольку клиент по чанку получает всего лишь число (int).

Цитировать
Qt::DirectConnection или Qt::QueuedBlockedConnection - в Вашем случае разницы никакой (я намеренно опускаю наличие межпоточной блокировки во втором случае). никакого пропуска данных там нет и не будет.

Если блокировка будет на время, не меньшее времени заполнения остатка буфера, то будет потеря данных на уровне взаимодействия источника данных со звуковухой (запрос на очередной чанк не будет выдан вовремя). На участке источник данных - клиент (что, по всей видимости, вы имели ввиду) потерь данных нет.

Цитировать
Как определить - ну а если незатейливо в run клиента запомнить его currentThreadId и проверять на неравенство текущему?

Да, и так вот незатейливо :) неравенство будет означать, что слот вызван из другой нитки, т.к. с QueuedConnection он может быть вызван только из своей!


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: LisandreL от Сентябрь 04, 2011, 03:25
Да, но это локальный участок, и пользователь должен не допускать этого. Ведь только ему известна "тяжесть" обработки. Так все коллбэки и строятся. Если обработка достаточно "тяжела", то пользователь должен вынести ее из слота и сделать асинхронной. Qt::DirectConnection в данном случае своего рода общий знаменатель.
Странно расчитывать, на то, что с тяжестью обработки пользователь сам разберётся, а с нужным типом коннекта - нет.


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Akon от Сентябрь 04, 2011, 08:20
Пользователь и не разбирается с типом коннекта - ему предлагается только DirectConnect. У себя в слоте, если необходимо, далее делается QueuedConnect.


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Igors от Сентябрь 04, 2011, 09:29
Пусть даже простейший вариант, без всяких оптимизаций/заморочек: клиент просто принимает все запросы. Почему сервер должен знать как толкнуть клиента. с каких мутексов его снять и.т.п? Более чисто смотрится "сервер уведомляет", а уж клиент разбирается как запуститься. И уведомление должно выполняться в нитке сервера (нитка клиента остановлена)

Да, и так вот незатейливо :) неравенство будет означать, что слот вызван из другой нитки, т.к. с QueuedConnection он может быть вызван только из своей!
Ну да, а разве Вам не это надо?


Название: Re: Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?
Отправлено: Akon от Сентябрь 04, 2011, 10:15
Ну да, а разве Вам не это надо?

Да, применительно к описанной мною задаче это должно работать, спасибо!

Изначальный вопрос ("Как проверить, что для данной пары сигнал/слот установлено Qt::DirectConnection?") трактуется несколько шире - источник данных может выдавать данные и в контексте потока клиента.