2016-05-19 3 views
1

У меня есть два потока, унаследованные от QThread. Сценарий - это объект потока2, излучающий сигнал, и объект thread1 имеет слот для выполнения. Я ожидал, что слоты выполняются в потоке2, но он выполняется в основном потоке !!!. Вот мой пример кода, чтобы показать, что:Политика выполнения слотов в многопоточных приложениях

Headrs.h

class Thread1 : public QThread{ 
    Q_OBJECT 
public: 
    Thread1(){} 
protected: 
    void run(){ 
     qDebug() << "run in thread " << this->currentThreadId(); 
     exec(); 
    } 
private slots: 
    void slot1(){ 
     qDebug() << "slot in " << this->currentThreadId(); 
    } 

signals: 
    void sig1(); 
}; 
class Thread2 : public QThread{ 
    Q_OBJECT 
protected: 
    void run(){ 
     msleep(100); 
     qDebug() << "emit sig2 in: " << this->currentThreadId(); 
     emit sig2(); 
    } 
signals: 
    void sig2(); 
}; 

class obj1 : public QObject { 
    Q_OBJECT 
public: 
    obj1(){ 
     connect(&t2, SIGNAL(sig2()), &t1, SLOT(slot1())); 
     connect(this, SIGNAL(sigObj()), &t1, SLOT(slot1())); 
     t1.start(); 
     t2.start(); 
    } 
public: 
    void fcn(){ 
     QThread::msleep(1000); 
     emit sigObj(); 
     qDebug() << "emit sigObj in " << QThread::currentThreadId(); 
    } 
private: 
    Thread1 t1; 
    Thread2 t2; 


signals: 
    void sigObj(); 
}; 

main.cpp

QCoreApplication a(argc, argv); 
obj1 o1; 
o1.fcn(); 

return a.exec(); 

Что я ожидаю от этого кода в Slot1() всегда выполняются в thread1 из оба излучаемых сигнала sig2() и sigObj(). Но независимо от того, в каком потоке мы испускаем сигнал, slot1 выполняется в основном потоке. Кстати, вот мой вывод:

run in thread 0x7ffff6169700 (thread1) 
emit sig2 in: 0x7ffff5968700 (thread2) 
slot in 0x7ffff7fce740  (main thread) 
emit sigObj in 0x7ffff7fce740 (main thread) 
slot in 0x7ffff7fce740  (main thread) 

Это что-то не так или всегда так работает? И что мне делать, если я хочу выполнить слоты в своих потоках?

+0

Пожалуйста, отметьте предложенный ответ, как принято (нажав на символ клещевого возле ответа), если он решает твоя проблема. – ixSci

ответ

3

Нет ничего особенного в отношении QThread. Это просто QObject, что происходит, а также быть ручкой для родной нити платформы. Но слоты объекта QThread работают так же, как если бы у вас был простой QObject. Они будут выполняться в потоке объекта - и это будет поток, в котором вы выполнили конструктор объекта или, в вашем случае, основной поток. Здесь все запутывает: thread() любого из ваших объектов потока по-прежнему остается основным потоком, и там будут работать слоты. Просто потому, что ваш QObject называется QThread не делает его другим.

Исправить это просто: не переопределяйте «Запуск» QThread и не добавляйте функциональность в QThread. Вместо этого явным образом перемещаю ваши объекты в потоки, где вы хотите, чтобы они выполняли свою работу. Это antipattern, чтобы явным образом называть moveToThread на QThread, поэтому не делайте этого как «быстрый взлом».

Единственная причина в этом случае получить от QThread - превратить его в соответствующий класс RAII, добавив quit(); wait(); в свой деструктор.

Вот как ваш код может выглядеть, исправлено:

// https://github.com/KubaO/stackoverflown/tree/master/questions/thread-sigslot-37325348 
#include <QtCore> 

class Worker1 : public QObject { 
    Q_OBJECT 
public: 
    Q_SIGNAL void sig1(); 
    Q_SLOT void slot1() { 
     qDebug() << "slot in" << thread(); 
    } 
}; 

class Worker2 : public QObject { 
    Q_OBJECT 
public: 
    Worker2() { 
     QTimer::singleShot(100, this, [this]{ 
     qDebug() << "emit sig2 in" << thread(); 
     emit sig2(); 
     }); 
    } 
    Q_SIGNAL void sig2(); 
}; 

class Object : public QObject { 
    Q_OBJECT 
    Worker1 w1; 
    Worker2 w2; 
    QThread t1, t2; 
    Q_SIGNAL void sig(); 
public: 
    Object() { 
     t1.setObjectName("t1"); 
     t2.setObjectName("t2"); 
     connect(&w2, &Worker2::sig2, &w1, &Worker1::slot1); 
     connect(this, &Object::sig, &w1, &Worker1::slot1); 
     w1.moveToThread(&t1); 
     w2.moveToThread(&t2); 
     t1.start(); 
     t2.start(); 
     QTimer::singleShot(1000, this, [this]{ 
     qDebug() << "emit sig in" << thread(); 
     emit sig(); 
     }); 
    } 
    ~Object() { t1.quit(); t2.quit(); t1.wait(); t2.wait(); } 
}; 

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    app.thread()->setObjectName("main_thread"); 
    Object obj; 
    QTimer::singleShot(2000, &app, [&]{ app.quit(); }); 
    return app.exec(); 
} 

#include "main.moc" 

Выход:

emit sig2 in QThread(0x7fff4fd98bd0, name = "t2") 
slot in QThread(0x7fff4fd98bc0, name = "t1") 
emit sig in QThread(0x7fe3dac0aed0, name = "main_thread") 
slot in QThread(0x7fff4fd98bc0, name = "t1") 
+0

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

+0

На самом деле, мой вопрос в том, не является ли другим способом и не использовать функцию moveToThread? –

+0

Думаю, я понял. Вы сказали, что не переместите thread1 в себя. как '' t1.moveToThread (&t1); '' правильно? –