Сигналы Qt (QueuedConnection и DirectConnection)

У меня проблемы с сигналами Qt.

Я не понимаю, как работают DirectConnection и QueuedConnection?

Я был бы признателен, если бы кто-нибудь объяснил, когда использовать какой из них (пример кода будет оценен).


person Nika    schedule 24.02.2013    source источник
comment
См. stackoverflow.com/questions/11230080/ и qt-проект .org/doc/qt-4.8/threads-qobject.html   -  person Mihai8    schedule 24.02.2013
comment
Что конкретно вам в них не понятно? Ваш вопрос сводится к тому, как сигналы и слоты работают без этого, что немного широко и хорошо освещено в документах.   -  person Mat    schedule 24.02.2013
comment
Хм... насколько я понимаю, QueuedConnection следует использовать, когда отправитель и получатель находятся в разных потоках. Например, у меня есть поток GUI (основной поток) и новый поток (pThread), который имеет сигнал, например, void doSomething(); и получатель является основным потоком графического интерфейса. Итак, я должен использовать QueuedConnection, неважно, где я буду его вызывать? (в потоке GUI или новом потоке, команда подключения) Спасибо ..   -  person Nika    schedule 24.02.2013
comment
Читайте документы в первом комментарии. Если вы не пытаетесь сделать что-то очень конкретное и полностью понимаете риски, вообще не указывайте режим подключения. По умолчанию будет использоваться правильный режим (прямой для внутрипотокового, поставленный в очередь для межпотокового).   -  person Mat    schedule 24.02.2013


Ответы (3)


Вы не увидите большой разницы, если только вы не работаете с объектами, имеющими разное сходство потоков. Допустим, у вас есть объекты QObject A и B, и они оба подключены к разным потокам. A имеет сигнал с именем somethingChanged(), а B имеет слот с именем handleChange().

Если вы используете прямое подключение

connect( A, SIGNAL(somethingChanged()), B, SLOT(handleChange()), Qt::DirectConnection );

метод handleChange() будет выполняться в потоке A. По сути, это как если бы излучение сигнала вызывало метод слота «напрямую». Если B::handleChange() не является потокобезопасным, это может привести к некоторым (трудно обнаруживаемым) ошибкам. По крайней мере, вы упускаете преимущества дополнительного потока.

Если вы измените метод подключения на Qt::QueuedConnection (или, в этом случае, пусть Qt решит, какой метод использовать), все становится интереснее. Предполагая, что поток B выполняет цикл событий, отправка сигнала отправит событие в цикл событий B. Цикл событий ставит событие в очередь и, в конце концов, вызывает метод слота всякий раз, когда к нему возвращается управление (это цикл событий). Это позволяет довольно легко иметь дело со связью между потоками в Qt (опять же, при условии, что ваши потоки выполняют свои собственные локальные циклы событий). Вам не нужно беспокоиться о блокировках и т. д., потому что цикл событий сериализует вызовы слотов.

Примечание: Если вы не знаете, как изменить привязку к потоку QObject, загляните в QObject::moveToThread. Это должно заставить вас начать.

Изменить

Я должен уточнить мое вступительное предложение. Это имеет значение, если вы указываете соединение в очереди — даже для двух объектов в одном потоке. Событие по-прежнему отправляется в цикл событий потока. Таким образом, вызов метода по-прежнему асинхронен, что означает, что он может быть отложен непредсказуемым образом (в зависимости от любых других событий, которые может потребоваться обработать циклу). Однако, если вы не укажете метод подключения, для соединений между объектами в одном потоке автоматически используется прямой метод (по крайней мере, в Qt 4.8).

person Jacob Robbins    schedule 13.03.2013
comment
@Jacob, так не лучше ли напрямую вызывать handleChange() как обычную функцию, вместо того, чтобы вдаваться в путаницу СИГНАЛЬНЫЙ СЛОТ для случая прямого подключения? - person Cool_Coder; 25.12.2013
comment
Cool_Coder: Преимущество вызова метода через соединение sig/slot заключается в том, что вам не нужно встраивать знания об объекте B в A — вы можете позволить пользователю двух классов определять, что должно произойти, когда somethingChanged испускается из A. Имеет ли это смысл? - person Jacob Robbins; 30.12.2013
comment
что произойдет, если я не укажу Qt::QueuedConnection в качестве параметра? Причина, по которой я спрашиваю об этом, заключается в том, что во многих местах указание Qt::QueuedConnection или direct является необязательным параметром. Означает ли это, что Qt сама принимает решение о том, поставить ли сигнал в очередь или вызвать напрямую? - person TheWaterProgrammer; 16.04.2018
comment
@Game_Of_Threads : аргумент по умолчанию — Qt::AutoConnection. Если эмиттер и приемник находятся в одном потоке, используется DirectConnection. В противном случае используется QueuedConnection. - person Jacob Robbins; 24.04.2018
comment
@JacobRobbins Не могли бы вы пояснить, почему нам не нужно использовать блокировки или синхронизацию при использовании Qt::QueuedConnection? Даже если два потока совместно используют некоторые данные (которые оба могут читать или записывать)? Или когда поток A передает некоторые данные через аргументы сигнала, который подключен к слоту в потоке B? - person today; 22.06.2018
comment
@today Если потоки обмениваются данными, да, вам нужно будет синхронизироваться. Так что не делайте этого без крайней необходимости. Использование QueuedConnection позволяет пользователям из многих потоков вызывать слоты в QObject, не беспокоясь о том, являются ли методы этого объекта реентерабельными (поскольку цикл событий сериализует вызовы). Имеет ли это смысл? - person Jacob Robbins; 22.06.2018
comment
@JacobRobbins Да, это так. Спасибо. На самом деле, я работаю над проектом, в котором я обрабатываю связь через последовательный порт в рабочем потоке. Пользователь может изменить настройки последовательного порта (например, скорость передачи), и я хотел сообщить рабочему потоку об этом изменении. Но я думаю, что никакого обмена данными здесь не происходит. Так что просто использование сигнала/слотов должно работать нормально. Использование мьютекса пришло мне в голову из-за примера в документах Qt. Но я думаю, что способ обработки последовательной связи в этом примере отличается от использования Qt::QueuedConnection. - person today; 23.06.2018

в дополнение к ответу Джейкоба Роббинса:

утверждение «Вы не заметите большой разницы, если только вы не работаете с объектами, имеющими разное сходство потоков» неверно;

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

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

Класс на основе QObject имеет соединение с самим собой в очереди

person t_3    schedule 12.10.2013
comment
Теперь я полностью понял сигналы и слоты, спасибо, что упомянули об этом в любом случае. - person Nika; 13.10.2013
comment
Хорошая точка зрения. Я всегда позволяю Qt выбирать метод подключения, если у меня нет веских причин выбирать тот или иной... так что я получаю прямое подключение. когда объекты находятся в одном потоке (используя 4.8). Это определенно имеет значение, если вы принудительно ставите соединение в очередь. Я отредактировал свой ответ, чтобы уточнить. - person Jacob Robbins; 30.12.2013

Ответ Джейкоба потрясающий. Я просто хотел бы добавить сравнительный пример для встроенного программирования.

Исходя из опыта работы со встроенными RTOS/ISR, было полезно увидеть сходство в Qt DirectConnection с вытесняющим поведением ISR и Qt QueuedConnection с сообщениями в очереди в RTOS между задачами.

Боковое примечание: исходя из опыта встраиваемых систем, мне трудно не определить поведение в программировании. Я никогда не оставляю спор как Авто, но это всего лишь личное мнение. Я предпочитаю, чтобы все было написано явно, и да, иногда это становится сложно!

person user3934527    schedule 29.06.2017
comment
Я не совсем уверен, что всегда лучше явно выбирать прямые соединения, а не соединения в очереди. В среде, где все происходит быстро, время, необходимое для того, чтобы вернуться и отрегулировать их по мере развития модели многопоточности, сделает ненулевой вероятность того, что прямое соединение может в конечном итоге быть задано для сигнала между потоками и в конечном итоге вызвать прерывистые происходит сбой на какой-то машине (состояние гонки на более быстром или медленном процессоре и т. д.). Просто говорю, что это добавляет риска во время разработки. - person Kim; 21.11.2019
comment
Я уважаю эту позицию, но мое мнение состоит в том, что я хочу знать, изменилось ли поведение после того, как модуль написан, и для меня самый простой способ - это компилятор щелкнет во время компиляции. Явно определяя переменную, я бы знал, как только поведение было изменено. Так что да, это может замедлить разработку, но я предпочитаю, чтобы через несколько месяцев выяснилось, что поведение изменилось и не работает с исходным дизайном. - person user3934527; 27.08.2020