LINUX.ORG.RU

[QT] signals/slots между тредами не понимаю


0

0

Смотрю пример применения тредов Mandelbrot.

http://doc.qt.nokia.com/4.6/threads-mandelbrot.html

Написано:

class MandelbrotWidget : public QWidget
{
//...
private slots:
    void updatePixmap(const QImage &image, double scaleFactor);
//...
}

class RenderThread : public QThread
{
//...
signals:
    void renderedImage(const QImage &image, double scaleFactor);

protected:
    void run();
//...
}

MandelbrotWidget::MandelbrotWidget(QWidget *parent)
    : QWidget(parent)
{
//...
    connect(&thread, SIGNAL(renderedImage(QImage,double)),
            this, SLOT(updatePixmap(QImage,double)));
//...
}


void RenderThread::run()
{
QImage image(resultSize, QImage::Format_RGB32);
 forever {
//...
emit renderedImage(image, scaleFactor);
//...
}
}


void MandelbrotWidget::updatePixmap(const QImage &image, double scaleFactor)
{
//...
pixmap = QPixmap::fromImage(image);
//...
}

Смущает что в сигнале/слоте передается указатель на локальную переменную image определенную в функции RenderThread::run(). Смущает потому что(на сколько я ничего не понимаю)при передаче сигнала между тредами не известно когда сигнал попадет в слот. Функция к тому времени может закончиться и локальной переменной придет кирдык. Я бы сделал image членом RenderThread чтоб она существовала пока существует объект. В данном случае все скорее будет работать чем нет, т.к. в RenderThread::run() все происходит внутри forever{}, но, кажется еще где то в примерах QT видел такую фигню. Это нормальна практика или проявление особенностей QT(e.g. reference count)?

Еще вопросы.

В этом примере в RenderThread::run() все происходит внутри forever{}. Правильно я понимаю что обработчик событий в этом треде не запущен ведь его запускает exec()?

А нафига треду обработчик событий синалы ведь будут работать по любому?

Как тред прибить по сигналу из другого треда?

Мда, короче ничего не понимаю, может почитать чего?

Передается не указатель, а константная ссылка. Если треды соединены (Blocking || QueuedConnection), то данные будут скопированы. Большинство Qt классов Implicitly Shared, поэтому копирование атомарно.

В этом примере в RenderThread::run() все происходит внутри forever{}. Правильно я понимаю что обработчик событий в этом треде не запущен ведь его запускает exec()?

Да, правильно.

А нафига треду обработчик событий синалы ведь будут работать по любому?

Не совсем понятно. что имелось в виду.

Как тред прибить по сигналу из другого треда?

connect(thread1, SIGNAL(error()), thread2, SLOT(quit()));

Мда, короче ничего не понимаю, может почитать чего?

Да, почитать :)

Kristi ()

>Правильно я понимаю что обработчик событий в этом треде не запущен ведь его запускает exec()?

ЕМНИП, eventloop есть у любого запущенного QThread и его наследников

А нафига треду обработчик событий синалы ведь будут работать по любому?

размечтался, только если у тебя Qt::DirectConnection

Как тред прибить по сигналу из другого треда?

очевидно натыкать в run кучу проверок на какой-нить volatile bool, впрочем если кто предложит что-то красивее я буду благодарен

antony986 ()

У вас верный ход мыслей.

Смущает потому что(на сколько я ничего не понимаю)при передаче сигнала между тредами не известно когда сигнал попадет в слот.


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

Я бы сделал image членом RenderThread чтоб она существовала пока существует объект.


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

Правильно я понимаю что обработчик событий в этом треде не запущен ведь его запускает exec()?


Да, события в этом потоке работать не будут.

А нафига треду обработчик событий синалы ведь будут работать по любому?


Да, все сигналы будут работать. Цикл событий нужен только для асинхронных слотов.

Как тред прибить по сигналу из другого треда?


Можно остановить цикл событий с помощью QThread::exit() (если он конечно был запущен). Но не факт, что поток прекратит работу - после exec() ведь может быть ещё код, который возьмёт да и запустит ещё один exec(). В мандельброте поток вообще работает вечно и корректно завершается (рзрывая тот самый бесконечный цикл) только с помощью флага abort в деструкторе.

Dendy ★★★★★ ()
Ответ на: комментарий от Kristi

Ваш ответ как бы говорит: «все работает потому что все копируется», но в какой момент и куда копируется не поясняет.

Передается не указатель, а константная ссылка.

Ну хорошо указатель != ссылка. На что будет ссылаться ссылка на локальную переменную после того как функция в кот. переменная определена кончится?

imatveev13 ()
Ответ на: комментарий от Dendy

>Да, события в этом потоке работать не будут.

Да, все сигналы будут работать. Цикл событий нужен только для асинхронных слотов.

да точно, это я тупой ((

antony986 ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.