Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: В каком контексте будет вызван слот
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Общие вопросы
flankerr
В главном потоке создаётся пользовательский поток. Из главного потока отсылается сигнал связанный со слотом класса потока пользователя. В контексте какого потока будет вызван слот ?
SABROG
Я думаю, что в контексте пользовательского потока, если в нем вызывается exec() в run(). Сигнал поставится в очередь и поток его обработает, когда завершит выполнение блокирующих операций.
flankerr
вот и я так думал а сейчас проверил и сильно обламался :(
если вызвать QThread::currentThreadId() в главном потке и в слоте класса дочернего потка то они совпадут а вот QThread::currentThreadId() вызванный в run будет другим.
connect вызывался и до вызова start и после, и до выхова exec дочернего потка и после (в главном потоке) в coonnect передавался тип Qt::QueuedConnection и оставлял по умолчанию - рзультат один - слот вызывается в контексте потока сигнала

точнее сказать слот будет вызван в контексте потока в котором был создан объект содержащий слот.
SABROG
Кажись вспоминаю, либо connect должен делаться в run() либо объект к которому коннектишся должен создаваться в run.
flankerr
опытным путём пришёл к тому что объект должен создоваться в run
igor_bogomolov
Цитата(flankerr)
опытным путём пришёл к тому что объект должен создоваться в run
Можно так же воспользоваться методом QObject::moveToThread
-----upd-----------------------------------------------------------------------------------------
Что то меня терзают смутные сомнения.

class ThreadSocket : public QThread
{
    Q_OBJECT
    QTcpSocket *socket;
public:
    ThreadSocket(QObject *parent = 0);
protected:
    void run();
private slots:
    void readSocket();
};
ThreadSocket::ThreadSocket(QObject *parent)
    : QThread(parent) {
}

void ThreadSocket::readSocket() {
    qDebug() << "read TP:" << QThread::currentThread();
    qDebug() << "read TID:" << QThread::currentThreadId();
}

void ThreadSocket::run() {
    socket = new QTcpSocket();
    connect(socket, SIGNAL(readyRead()), SLOT(readSocket()));
    exec();
}

Может я уже что то подзабыл. Но мне кажется, что в таком случае слот readSocket будет вызван в главном(GUI) потоке.
Для того чтобы слот вызвался в дочернем потоке, нужно в коннект добавить Qt::DirectConnection.
Или в конструктор класса добавить moveToThread(this);
SABROG
А объясните мне такую вещь. Если запустить timerEvent для объекта в главном потоке и в нем эмитить сигнал для дочернего потока, то слот этого потока выполняется в главном потоке (видимо причина в том, что сам объект QThread находится в главном потоке). Однако, если эмитить сигнал из run() для главного потока, то слот вызывается в главном потоке (что есть правильно).

Вопрос в том как отправить сигнал дочернему потоку из объекта главного потока, чтобы слот выполнялся в дочернем потоке? При этом moveToThread или создание того же QMainWindow внутри run() совершенно не выход. В первом случае вернутся из moveToThread не получится, а второй случай вообще бредовый.
Litkevich Yuriy
Цитата(SABROG @ 11.6.2009, 20:19) *
слот этого потока
правильнее пожалуй будет - слот объекта. Ну а в каком потоке объект в том и слот. Помоему всё так.
SABROG
Цитата(Litkevich Yuriy @ 11.6.2009, 17:31) *
Ну а в каком потоке объект в том и слот.


Только получается, что оба объекта созданы в главном потоке. Как же тогда осуществлять межпоточное взаимодействие типа "юзер кликнул - пошел сигнал в дочерний поток, вызвался долгоиграющий слот".
Litkevich Yuriy
Надо поискать по форуму. Эдик (ViGOur) с кем-то обсуждал эту тему в подробностях.

вроде вот эта
SABROG
Цитата(Litkevich Yuriy @ 11.6.2009, 17:52) *
вроде вот эта


Прикол в том, что я сам себе ответил. Однако проблема остается, если эмитить сигнал из главного потока в дочерний, то слот на этот сигнал выполнится в главном потоке. Это ведь не есть правильно, блокировать основной поток. Интересно как это можно обойти используя стандартные средства сигналов/слотов и цикла событий.
Litkevich Yuriy
Я с потоками работал толькот одним способом:
Переопределяю в наследнике метод run(), из главного вызваю start().
Шлю в поток сигнал, управление сразу возвращается в главный поток.
SABROG
Попытался воссоздать ситуацию:

#include <QtCore/QtGlobal>
#include <QtCore/QtDebug>
#include <QtCore/QCoreApplication>
#include <QtCore/QThread>

class Object : public QObject
{
    Q_OBJECT
public:
    Object(QObject *parent = 0) : QObject(parent) {}
    virtual ~Object() {}
public slots:
    void objectSlot()
    {
        qDebug() << "message from thread. Cur thread:" << thread()->currentThreadId();
    }
signals:
    void objectSignal();
protected:
    void timerEvent(QTimerEvent *event)
    {
        qDebug() << thread()->currentThreadId();
        emit objectSignal();
    }
};

class Thread : public QThread
{
    Q_OBJECT
public:
    Thread(QObject *parent = 0) : QThread(parent) {}
    void run()
    {
        parent()->connect(this, SIGNAL(threadSignal()), SLOT(objectSlot()));
        qDebug() << "try emit from thread: " << currentThreadId();
        emit threadSignal();
        exec();
    }
signals:
    void threadSignal();
        
public slots:
    void threadSlot()
    {
        qDebug() << "thread: " << currentThreadId();
        //quit();
    };
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Object object;
    Thread thread(&object);
    thread.connect(&object, SIGNAL(objectSignal()), SLOT(threadSlot()), Qt::QueuedConnection);
    thread.start();
    object.startTimer(1000);
    return a.exec();
}

#include "main.moc"


Хотел сделать что-то общения между потоками посредством сигналов. Главный поток пытается раз в секунду послать сигнал в дочерний поток, но слот вызывается в том же главном потоке несмотря на насильную установку флага Qt::QueuedConnection. Т.е. как ни крути, а все слоты вызываются из гуишного потока. А я хотел, чтобы цикл событий дочернего потока принял эвент о сигнале в run() и выполнил слот в своем потоке.
Litkevich Yuriy
Цитата(SABROG @ 11.6.2009, 21:19) *
Т.е. как ни крути, а все слоты вызываются из гуишного потока.
как ты такой вывод сделал?
BRE
Цитата(SABROG @ 11.6.2009, 18:08) *
Однако проблема остается, если эмитить сигнал из главного потока в дочерний, то слот на этот сигнал выполнится в главном потоке. Это ведь не есть правильно, блокировать основной поток. Интересно как это можно обойти используя стандартные средства сигналов/слотов и цикла событий.

Исходные данные: в главном потоке создается дочерний поток. Тут нужно сразу понять, что объект (!) дочернего потока (QThread и его наследники) создается в главном потоке и находится в контексте главного потока. Контекст дочернего потока образуется только после запуска потока (QThread::start). Контекст дочернего потока состоит из метода run(), все объекты созданные в нем, будут располагаться в контексте дочернего потока.
Теперь вспомним как Qt работает с межпоточными сигналами: при вызове сигнала, слот-обработчик которого находиться в другом контексте, Qt поместит специальное событие в очередь потока получателя и при очередном кванте времени из метода exec будет вызван нужный слот. Т.к. exec крутиться в контексте дочерней нити этот слот будет вызван в его контексте.

Почему же происходит что слот нити выполняется в главном? Для Qt объект дочерний нити находиться в контексте главной нити, поэтому сигнал-слот связывается на прямую и соответствено вызывается в контексте главной нити.
SABROG
Цитата(Litkevich Yuriy @ 11.6.2009, 18:27) *
Цитата(SABROG @ 11.6.2009, 21:19) *
Т.е. как ни крути, а все слоты вызываются из гуишного потока.
как ты такой вывод сделал?


Я решил поэкспериментировать и пришел к такому вот странному решению:

thread.moveToThread(&thread);


Я назвал этот метод методом "Мюнхаузена" :lol:

Теперь действительно сигналы отправляются из главного потока и слот вызывается в дочернем потоке.
BRE
Цитата(SABROG @ 11.6.2009, 18:36) *
Я решил поэкспериментировать и пришел к такому вот странному решению:

thread.moveToThread(&thread);


Я назвал этот метод методом "Мюнхаузена" :lol:

Теперь действительно сигналы отправляются из главного потока и слот вызывается в дочернем потоке.

Этот метод был разработан несколькими днями ранее kuzulis. ;)
http://www.prog.org.ru/topic_9694_0.html
Litkevich Yuriy
Цитата(SABROG @ 11.6.2009, 21:36) *
Я назвал этот метод методом "Мюнхаузена"
Цитата(BRE @ 11.6.2009, 21:41) *
Этот метод был разработан несколькими днями ранее kuzulis.
на что Константин сказал:
Цитата
улыбнуло...ассоциируется с картинкой, где чел сам себе в зад залазит
SABROG
Цитата(BRE @ 11.6.2009, 18:41) *
Этот метод был разработан несколькими днями ранее kuzulis.


Если верить его словам, то даже не им, он где-то это увидел :)

Жаль авторство не могу себе присвоить ^_^ , хотя конечно решение самом себе в голову напрашивается.

Улыбнула фраза Константина

Цитата
собсна, вообще не удивительно.
а вот tc->moveToThread(tc) улыбнуло...ассоциируется с картинкой, где чел сам себе в зад залазит /* может видел кто? */ Улыбающийся


Ну хотябы могу себе название метода "Мюнхгаузена" присвоить ;)

---
Кстати, скорее всего человек увидел этот метод отсюда http://forum.vingrad.ru/forum/topic-241300...y1737536/0.html Этот топик один из первых выдается в гугле на поиск moveToThread.

Еще немного погуглил и нашел такую переписку с канала #Qt:

Цитата
[04:58] <daniel> thiago_home: Yeah I just noticed I don't have to wait.
[04:59] <daniel> Is "thread.start(); thread.moveToThread(&thread);" ok?
[05:00] <thiago_home> yes


Собственно, я так понимаю, что thiago_home это Thiago Macieira, раз он одобряет этот метод, то почему бы троллям не предложить ввести что-то типа флага, чтобы объект сразу сам в себя перемещался внутри метода start().

wasyota тоже такой метод предлагает.
---
Может поместить это решение в "Секреты и интересные возможности" ?
igor_bogomolov
Цитата(Litkevich Yuriy @ 11.6.2009, 18:46) *
Жаль авторство не могу себе присвоить , хотя конечно решение самом себе в голову напрашивается.
Себе авторство тоже присвоить не могу, но если посмотришь здесь (в атаче), увидишь, что я пришел к такому же решению. Я тогда только разбирался в этом вопросе, и сделал так скорее по незнанию. Я предлогаю делать так
ThreadSocket::ThreadSocket(QObject *parent)
    : QThread(parent)
{
    moveToThread(this);
}
На мой взгляд, это будет правильнее.
BRE
Я к самому классу QThread и его наследникам отношусь немного по другому.
Объект этого класса использую только для управления потоком, я не расширяю его функциональность.
Т.е. есть методы управления и проверки состояния потока (с ними работа идет из запустившего потока) и как-бы отдельно метод run, который и описывает сам поток. Вот в нем я создаю необходимые объекты связываю их сигналами с объектами в других потоками и т.д.
Управление идет через объект находящийся в контексте родительского (основного) потока, а что там в потоке делается это уже к run().

Блин, не знаю как получилось описать. :rolleyes:
С точки зрения архитектуры подхожу к объектам потоков по другому... :)
SABROG
Цитата(BRE @ 11.6.2009, 21:44) *
С точки зрения архитектуры подхожу к объектам потоков по другому...


Получается, чтобы связать слот в потоке и сигнал из главного окна тебе приходится создавать дополнительный объект в run() к которому все и привязывать?
Litkevich Yuriy
Цитата(SABROG @ 11.6.2009, 21:56) *
Может поместить это решение в "Секреты и интересные возможности" ?
блин странно это всё.
BRE
Цитата(SABROG @ 11.6.2009, 21:46) *
Цитата(BRE @ 11.6.2009, 21:44) *
С точки зрения архитектуры подхожу к объектам потоков по другому...


Получается, чтобы связать слот в потоке и сигнал из главного окна тебе приходится создавать дополнительный объект в run() к которому все и привязывать?

Да, я не добавляю слоты в класс наследник от QThread (не расширяю его функциональность).
А как ты и написал делаю отдельный класс, который содержит сигналы/слоты и он выполняет всю работу, его создаю в run и настраиваю связи.
SABROG
Цитата(Litkevich Yuriy @ 11.6.2009, 21:48) *
Цитата(SABROG @ 11.6.2009, 21:56) *
Может поместить это решение в "Секреты и интересные возможности" ?
блин странно это всё.

Может кто с QtConcurrent работал, там может быть можно иначе сделать?
Litkevich Yuriy
Собственно почему странно, если так можно самого "потока" запнуть в самого себя, то почему он сам этого не делает.
Т.е. должно было бы быть так:
QThread th(...); // Уже завертелся новый поток, но пока безработный. А может и сразу работу выполняет.
и все функции, объявленные в нём, уже в другом потоке.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.