2016-07-14 8 views
1

У меня возникли трудности с пониманием того, что происходит, например, напр. два разных сигнала подключаются к двум разным слотам, и когда один слот не выполняется, выдается сигнал других слотов (для обоих слотов, подключенных к их соответствующим сигналам в прямом соединении), где приложение имеет только «одну» нить.Сигналы QT и слоты для прямого соединения в приложении с одной нитью

Это из официальной документации QT:

Прямое соединение: Слот вызывается сразу же, когда сигнал. Слот выполняется в потоке эмиттера, что не обязательно является потоком получателя.

Очередное соединение: слот вызывается, когда управление возвращается в цикл событий потока получателя. Слот выполняется в потоке получателя.

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

Возможно, я недопонимаю всю вещь «Прямая» и «Очередь».

+0

Невозможно сделать что-либо по-настоящему одновременно в той же теме. Нить имеет один локус управления. То, как вы его просите, - это бессмыслица, в значительной степени. «Немедленно» означает «слоты называются последовательно от тела сигнала». Сигнал - это метод С ++, а не магия. Он перечисляет непосредственно подключенные слоты/функторы и вызывает их. Он перечисляет все подключенные слоты/функторы, помещенные в очередь, и помещает 'QMetaCallEvent' в свои объекты контекста. **Это все**. –

ответ

2

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

Прямой/Queued

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

  • Если это прямое соединение, ваш код ВСЕГДА выполняется в одном потоке независимо от того, выполняется ли слот в другом потоке.(В большинстве случаев идея ОЧЕНЬ ПЛОХАЯ)
  • Если соединение поставлено в очередь и слот работает внутри одной и той же нити, он действует точно, как прямое соединение. Если слот работает в другом потоке, слот будет выполняться в пределах своего соответствующего контекста потока, в котором он запущен, поэтому выполнение кода выполняется многопоточно.
  • Если соединение auto по умолчанию, по уважительной причине, то оно выбрало подходящий тип подключения.

Теперь есть способ, чтобы заставить ваше выполнение кода, чтобы перейти в слот в другом потоке, то есть с помощью вызова метода:

QMetaObject::invokeMethod(pointerToObject*, "functionName", Qt::QueuedConnection); 

основ

Так let' s быстро перейдете через механизм сигнала/слота. В системе есть три участника: сигналы, слоты и подключение()

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

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

Достаточно разглагольствований, давайте идти вниз к некоторым практическим особенностям:

signals: 
    void a(); 
    void b(); 
slots: 
    void sa(); 
    void sb(); 

Случай 1

connect(a -> sa); // Simplified Notation. Connect signal a to slot sa 
connect(b -> sb); 

:: emit a(); -> sa is executed 
:: emit b(); -> sb is executed 

Случай 2

connect (a -> sa); 
connect (a -> sb); 
connect (b -> sb); 

:: emit a(); -> sa & sb are executed 
:: emit b(); -> sb is executed 

Я думаю, что это должно прояснить ситуацию. Если возникло вопросов дайте мне знать

1

Прямое подключение означает, что излучение сигнала и вызов слота сокращены до простого вызова метода, в результате чего звонок вызова emit происходит непосредственно в слот. Очередное подключение помещает вызов в очередь, которая обрабатывается, как только цикл событий Qt запускается снова, или вы принудительно вызываете его, вызывая QCoreApplication::processEvents(), в соответствии со всеми остальными событиями, которые были поставлены в очередь до тех пор.

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

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

1

Прямое соединение точно как вызов функции (метода) указателя. Там нет «прерывание», если Вы не думаете, что в коде ниже printf() «перебивает» main():

// main.cpp 
#include <cstdio> 
int main() { 
    printf("Hello\n"); 
} 

Все код выполняется в том же потоке. main() и printf() никогда не запускаются одновременно: в то время как printf() работает, main() приостановлено. Однажды printf() возвращается, main() резюме. То же самое происходит с слотами/функторами, напрямую связанными с сигналами.

Например, у нас есть следующие Object и класс Monitor, чтобы визуализировать, что происходит.

// https://github.com/KubaO/stackoverflown/tree/master/questions/sigslot-nest-38376840 
#include <QtCore> 
struct Monitor { 
    int & depth() { static int depth = 0; return depth; } 
    const char * const msg; 
    Monitor(const char * msg) : msg{msg} { 
     qDebug().noquote().nospace() << QString(depth()++, ' ') << msg << " entered"; 
    } 
    ~Monitor() { 
     qDebug().noquote().nospace() << QString(--depth(), ' ') << msg << " left"; 
    } 
}; 
struct Object : QObject { 
    Q_SIGNAL void signal1(); 
    Q_SIGNAL void signal2(); 
    Q_SLOT void slot1() { Monitor mon{__FUNCTION__}; } 
    Q_SLOT void slot2() { Monitor mon{__FUNCTION__}; } 
    Q_SLOT void slot3() { 
     Monitor mon{__FUNCTION__}; 
     emit signal2(); 
    } 
    Q_OBJECT 
}; 

Давайте signal1 подключены непосредственно к слотам slot1, slot2 и slot3. Кроме того, давайте signal2, подключенного непосредственно к слотам slot1 и slot2:

int main() { 
    Monitor mon{__FUNCTION__}; 
    Object obj; 
    QObject::connect(&obj, &Object::signal1, &obj, &Object::slot1); 
    QObject::connect(&obj, &Object::signal1, &obj, &Object::slot2); 
    QObject::connect(&obj, &Object::signal1, &obj, &Object::slot3); 
    QObject::connect(&obj, &Object::signal2, &obj, &Object::slot1); 
    QObject::connect(&obj, &Object::signal2, &obj, &Object::slot2); 
    emit obj.signal1(); 
} 
#include "main.moc" 

Выходной сигнал становится ясно, что любые слоты непосредственно подключены к сигналам, которые вы испускают выполняются в то время как сигнал запуска. Кроме того, для работы подключенных слотов/функторов не требуется никакого цикла событий. Наконец, помните, что сигнал является «просто» способом, созданным moc, который вызывает все связанные с подключением слоты/функторы.

main entered 
slot1 entered 
slot1 left 
slot2 entered 
slot2 left 
slot3 entered 
    slot1 entered 
    slot1 left 
    slot2 entered 
    slot2 left 
slot3 left 
main left