Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: Fess от Июнь 02, 2011, 06:10



Название: QueuedConnection
Отправлено: Fess от Июнь 02, 2011, 06:10
Извиняюсь за тупой вопрос, но все же.. Объясните пожалуйста, в чем магия?
Минимальный пример:
Код:
#include <QtGui/QApplication>
#include <QThread>
#include <QDebug>
#include <QTimer>
#include <QDialog>

class Dialog : public QDialog {
Q_OBJECT

public:
explicit Dialog(QWidget *parent = 0)
: QDialog(parent)
{

}
};

class Tester : public QObject {
Q_OBJECT
protected:
int i;
QTimer timer;
public:
Tester(QObject* parent = NULL)
: QObject(parent)
, i(0)
{
timer.setInterval(3000);
connect(&timer,SIGNAL(timeout()),this,SLOT(RunDialog()),Qt::QueuedConnection);
timer.start();
}

public slots:
void RunDialog() {
qDebug()<<"run in "<<QThread::currentThreadId();
Dialog w;
w.setWindowTitle(QString("Dialog %1 from %2").arg(i++).arg(QString::number((long)QThread::currentThreadId(),16)));
w.exec();
qDebug()<<"Dialog \""<<w.windowTitle()<<"\" closed";
}
};

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(false);
qDebug()<<"main thread "<<QThread::currentThreadId();
Tester tester;
return a.exec();
}

#include "main.moc"

Каждые 3 секунды посылается сигнал на создание нового диалога и проходит через event loop. Диалог запускается как модальный и по идее выполнение главного потока должно останавливаться пока он не будет закрыт и не будет получен результат от exec().
Но вместо этого постоянно создаются новые диалоги.
Если сделать соединение сигнал-слот DirectConnection, то в каждый момент может быть запущен только один диалог.
Я хочу понять в чем причина такого поведения при QueuedConnection.


Название: Re: QueuedConnection
Отправлено: LisandreL от Июнь 02, 2011, 06:43
Диалог запускается как модальный и по идее выполнение главного потока должно останавливаться пока он не будет закрыт и не будет получен результат от exec().
Поток у них один и тот же. Весь GUI живёт в одном потоке.
А разница, скорее всего в реализации QTimer. Для QTimer разница следующая: при прямом соединении он не выходит из QTimer::timerEvent, а так и стоит на emit timeout, а при соединении очереди - выходит.
Почему не программа не заходит повторно в timerEvent, если в предыдущий раз не вышла? Видимо есть в обработчике событий некоторая защита...


Название: Re: QueuedConnection
Отправлено: Fess от Июнь 02, 2011, 07:29
QTimer здесь для примера. В моем случае сигналы приходили по сети из другого потока, но поведение аналогичное.
Мне интересно вот с этого момента:
Цитировать
Qt::QueuedConnection:
The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
управление вернулось в event loop, заметило что пришел упакованный в событие сигнал, запустило слот. Слот управление не возвращает и главный поток должен ждать выхода из exec. Однако, события продолжают приходить и слот выполняется снова и снова.
Допустим, event loop работает в отдельном потоке, но слот-то каждый раз выполняется в одном и том же!


Название: Re: QueuedConnection
Отправлено: LisandreL от Июнь 02, 2011, 07:54
Слот управление не возвращает и главный поток должен ждать выхода из exec.
Ну как вам объяснить? Ну нету там главного и неглавного потока. Весь гуй живёт в одном потоке, хоть 100500 модальных диалогов создайте. В модальном диалоге события обрабатываются, значит они обрабатываются и во всём потоке. Именно поэтому, скажем, неактивные (из-за модальности диалога) окна не перестают перерисовываться (то есть для них происходит paintEvent). Просто QEventLoop::ExcludeUserInputEvents в них не принимаются.


Название: Re: QueuedConnection
Отправлено: GreatSnake от Июнь 02, 2011, 11:30
Модальность окна ни в коей мере не влияет на генерацию событий, но влияет на доставку некоторых.


Название: Re: QueuedConnection
Отправлено: Igors от Июнь 02, 2011, 12:04
Если кратко то "Qt так работает"  :)
С DirectConnection вызов слота происходит через QMetaObject::activate, который захватывает мутекс объекта, поэтому дальнейшие сигналы (посылаемые ему через direct) не проходят пока мутекс не освобожден. С QueuedConnection вызов идет через placeMetaCall, который такой блокировки не имеет.

Практически "этажерка" модальных диалогов ничего хорошего не обещает, лучше разобраться по одному, т.е. проверять уже выскочили с модальностью или как

Edit: насчет мутекса это я загнул  :) Там проверяется через signal_begin_callback/signal_end_callback - но суть та же