Qt中实现多线程往往是构造一个数据处理类(继承Qobject),然后新建一个对象,将其依附于子线程(通过movetothread(QThread*)成员函数),通过signal-slot实现多线程。为何不是新建一个数据处理线程类来实现这个过称?
connect函数第五个参数
要回答上面的问题,先简单介绍connect函数,官方文档
[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type = Qt::AutoConnection)
第五个参数Qt::ConnectionType type = Qt::AutoConnection代表的是信号与槽的连接方式
AutoConnection
如果信号在接收者所依附的线程内发射,则等同于直接连接
如果发射信号的线程和接受者所依附的线程不同,则等同于队列连接
DirectConnection
当信号发射时,槽函数将直接被调用。
无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行。
QueuedConnection
当控制返回到接收方线程的事件循环时,将调用槽。插槽在接收方的线程中执行。
BlockQueuedConnection
与Qt::QueuedConnection相同,只是发出信号的线程会阻塞,直到槽返回。如果接收方处于发送信号的线程中,则不能使用此连接,否则应用程序将死锁。
QThread
QThread是管理线程的类,它所依附的线程和管理的线程不一样。比如
QThread 所依附的线程,就是执行 QThread t(0) 或 QThread * t=new QThread(0) 的线程。
QThread 管理的线程,就是 run 启动的线程。也就是次线程
定义一个 Dummy 类,用来发信号
定义一个 Thread 类,用来接收信号
重载 run 函数,目的是打印 threadid
/*!
* \file main.cpp
*
* Copyright (C) 2010, dbzhang800
* All rights reserved.
*
*/
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QDebug>
class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(){}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
};
class Thread:public QThread
{
Q_OBJECT
public:
Thread(QObject* parent=0):QThread(parent)
{
//moveToThread(this);
}
public slots:
void slot_main()
{
qDebug()<<"from thread slot_main:" <<currentThreadId();
}
protected:
void run()
{
qDebug()<<"thread thread:"<<currentThreadId();
exec();
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
Thread thread;
Dummy dummy;
QObject::connect(&dummy, SIGNAL(sig()), &thread, SLOT(slot_main()));
thread.start();
dummy.emitsig();
return a.exec();
}
结果
main thread: 0x1a40 from thread slot_main: 0x1a40 thread thread: 0x1a48
槽函数的线程和主线程是一样的!
如果想让槽函数slot在次线程运行(比如它执行耗时的操作,会让主线程死掉),怎么解决呢?
注意:dummy信号是在主线程发射的, 接收者 thread 也在主线程中。
参考我们前面的结论,很容易想到:
将 thread 依附的线程改为次线程不就行了?
这也是代码中注释掉的 moveToThread(this)所做的,去掉注释,你会发现slot在次线程中运行
结果
main thread: 0x13c0
thread thread: 0x1de0
from thread slot_main: 0x1de0
这可以工作,但这是 Bradley T. Hughes 强烈批判的用法。
推荐的方法
定义一个 Dummy 类,在run中发射它的信号
也可以在run中发射 Thread 类中的信号,而不是Dummy(效果完全一样)
QThread 定义槽函数,重载run函数
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QDebug>
class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(QObject* parent=0):QObject(parent) {}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
};
class Object:public QObject
{
Q_OBJECT
public:
Object(){}
public slots:
void slot()
{
qDebug()<<"from thread slot:" <<QThread::currentThreadId();
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
QThread thread;
Object obj;
Dummy dummy;
obj.moveToThread(&thread);
QObject::connect(&dummy, SIGNAL(sig()), &obj, SLOT(slot()));
thread.start();
dummy.emitsig();
return a.exec();
}
结果
main thread: 0x1a5c
from thread slot: 0x186c
这样通过定义数据处理类(继承自QObject),通过movetothread依附于不同的线程,再通过connect函数可以灵活控制槽函数在哪个线程运行