一个QObject实例是具有线程依附性的,或者说它驻留在某个线程。当一个OQbject接收到队列信号(queued signal)或者投递事件(posted event),槽函数或者事件处理函数运行在对象驻留的线程中。
注意:如果一个QObject没有线程依附性(也就是说thread()函数返回0),或者它位于没有运行事件循环的线程内,那么它就不能接收到队列信号或者投递事件。
一个QObject默认是位于创建它的线程内。一个对象的线程依附性可以使用thread()查询,并且可以通过moveToThread()改变。
所有的QObjects必须和它们的父类位于同一个线程。所以:
1)如果牵涉到的两个QObject对象位于不同的线程,setParent()会失败。
2)当一个QObject对象移动到另一个线程,它的所有子类也会自动移动。
3)如果QObject对象有父类,moveToThread()会失败。
4)如果QObject对象在QThread::run()函数内创建,它们不可以成为QThread对象的子类,因为QThread并不是驻留在调用QThread::run()的线程内。
注意:一个QObject的成员变量不会自动成为它的子类。父—子关系必须通过传递指针给子类的构造函数,或者调用setParent()函数来设置。没有这一步,当moveToThread()被调用时,对象的成员变量仍旧会留在旧的线程中。
下面是示例代码,用来验证线程依附性:
/* threadaffinity.h */
#ifndef THREADAFFINITY
#define THREADAFFINITY
#include <QThread>
class Thread : public QThread
{
Q_OBJECT
signals:
void sendSignal();
protected:
void run();
};
class Object : public QObject
{
Q_OBJECT
public slots:
void receiveSlot();
};
#endif // THREADAFFINITY
/* threadaffinity.cpp */
#include <QDebug>
#include <QTimer>
#include "threadaffinity.h"
void Object::receiveSlot()
{
QTimer* timer = new QTimer;
qDebug() << "Object::receiveSlot " << timer->thread();
delete timer;
}
void Thread::run()
{
QTimer* timer = new QTimer;
qDebug() << "Thread::run " << timer->thread();
emit sendSignal();
delete timer;
}
/* main.cpp */
#include "threadaffinity.h"
#include <Qtcore/QCoreApplication>
#include <QDebug>
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
Thread thread;
Object object;
qDebug() << "main " << app.thread();
qDebug() << "thread " << thread.thread();
qDebug() << "object " << object.thread();
QObject::connect(&thread, SIGNAL(sendSignal()), &object, SLOT(receiveSlot()));
thread.start();
return app.exec();
}
输出结果如下:
先说一下跨线程的信号和槽机制,Qt支持5种信号-槽连接类型:
1)自动连接(默认) 如果信号在接收对象具有依附性的线程内发射,那么行为和直接连接一样。否则,行为和 队列连接一样。
2)直接连接 如果信号被发射,槽立即被调用。槽在发射者线程内执行,并不一定在接收者线程内执 行。
3)队列连接 当控制权返回给接收者线程的事件循环时槽被调用。槽在接收者线程内执行。
4)阻塞队列连接 槽被调用的方式和队列连接一样,除了当前线程会阻塞直到槽返回。
注意:在同个线程使用这种类型连接对象会造成死锁。
5)唯一连接 行为和自动连接一样,但是只有它没有复制现有连接的时候才会进行连接。换言之,对于 同一对对象,如果相同的信号已经连接到相同的槽,那么不会进行连接,connect()返回false。
由输出结果可知,thread和object的线程依附性相同,他们隶属于地址为0x8835a8的线程,也就是main函数所在的线程。这正说明了一个QObject对象默认是位于创建它的线程内,thread和object对象正是在主线程内创建的。object对象的槽函数也属于地址为0x8835a8的线程,因为我们使用的是自动连接,而接收对象和发射信号所在的线程并没有依附性(可以通过moveToThread()改变),所以会使用队列连接,这时槽会在接收者线程执行,也就是在地址为0x8835a8的主线程执行。只有发射者才真正在自己类(Thread类)所在的线程运行。