多线程应用程序中的槽执行策略
Slots execution policy in multithread applications
我有两个线程继承自QThread。该场景是 thread2 的对象发出信号,而 thread1 的对象有一个要执行的槽。我希望插槽在 thread2 中执行,但它在主线程中执行!!!。这是我的示例代码来表明:
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() 始终在线程 1 中从发出的信号 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)
是哪里出了问题还是一直这样?还有我想在自己的线程中执行slot怎么办?
QThread
没什么特别的。它只是 QObject
碰巧 也是平台本地线程的句柄。但是 QThread
派生对象的插槽就像你有一个普通的 QObject
一样工作。它们将在对象的线程中执行——这将是您执行对象构造函数的线程,或者在您的情况下是主线程。这就是令人困惑的地方:您的任一线程对象的 thread()
仍然是主线程,这就是插槽 运行 的位置。仅仅因为您的 QObject
被称为 QThread
并没有什么不同。
解决方法很简单:不要覆盖 QThread
的 "run",也不要向 QThread
添加功能。相反,明确地将您的对象移动到您希望它们完成工作的线程。在 QThread
上显式调用 moveToThread
是一种反模式,所以不要像 "quick hack".
那样做
在这种情况下,从 QThread
派生的唯一原因是通过向其析构函数添加 quit(); wait();
将其变成适当的 RAII class。
修复后的代码可能如下所示:
// https://github.com/KubaO/Whosebugn/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")
我有两个线程继承自QThread。该场景是 thread2 的对象发出信号,而 thread1 的对象有一个要执行的槽。我希望插槽在 thread2 中执行,但它在主线程中执行!!!。这是我的示例代码来表明:
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() 始终在线程 1 中从发出的信号 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)
是哪里出了问题还是一直这样?还有我想在自己的线程中执行slot怎么办?
QThread
没什么特别的。它只是 QObject
碰巧 也是平台本地线程的句柄。但是 QThread
派生对象的插槽就像你有一个普通的 QObject
一样工作。它们将在对象的线程中执行——这将是您执行对象构造函数的线程,或者在您的情况下是主线程。这就是令人困惑的地方:您的任一线程对象的 thread()
仍然是主线程,这就是插槽 运行 的位置。仅仅因为您的 QObject
被称为 QThread
并没有什么不同。
解决方法很简单:不要覆盖 QThread
的 "run",也不要向 QThread
添加功能。相反,明确地将您的对象移动到您希望它们完成工作的线程。在 QThread
上显式调用 moveToThread
是一种反模式,所以不要像 "quick hack".
在这种情况下,从 QThread
派生的唯一原因是通过向其析构函数添加 quit(); wait();
将其变成适当的 RAII class。
修复后的代码可能如下所示:
// https://github.com/KubaO/Whosebugn/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")