很久之前,在使用QThread过程中,继承QThread和重载它的run()函数是唯一推荐的方式。这种用法是很很直观和简单的。但是,当在工作线程中使用SLOTS和Qtevent 循环时,一些用户往往会犯一些错误。所以,作为Qt的核心成员之一的Bradley T. Hughes,++推荐大家大家使用 QObject::moveToThread 把工作对象加入线程中++。不过,一些用户仍然对之前的用法进行了改革。所以,前Qt核心开发人员Olivier Goffart,告诉那些指着与继承Qthhread的用户:你并没有用错。最终,我们可以在QTread的人当中看到两种不同的用法。
从Qt官方文档,我们可以看出:
A QThread instance represents a thread and provides the means to start() a thread, which will then execute the reimplementation of QThread::run(). The run() implementation is for a thread what the main() entry point is for the application.
QThread::run() 是现成的入口点
继承Qthread然后重载run()
例如:
#include
class Thread : public QThread { private: void run() { qDebug()<<"From worker thread: "<() ; } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug()<<"From main thread: "<<QThread::currentThreadId(); Thread t; QObject::connect(&t, SIGNAL(finished()), &a, SLOT(quit())); t.start(); return a.exec(); }
输出如下:
From main thread: 0x15a8
From worker thread: 0x128c
因为QThread::run()是线程的入口,所以,没在在run()函数里调用的所有代码,都不会再工作线程里面执行。
在下面的例子中,成员变量m_stop将会被 stop() and run()访问和存储。假设前者将在主线程中执行,而后者则在工作线程中执行,则需要使用互斥锁或其他措施。
#if QT_VERSION>=0x050000
#include
#else
#include
#endif
class Thread : public QThread
{
Q_OBJECT
public:
Thread():m_stop(false)
{}
public slots:
void stop()
{
qDebug()<<"Thread::stop called from main thread: "<true;
}
private:
QMutex m_mutex;
bool m_stop;
void run()
{
qDebug()<<"From worker thread: "<while (1) {
{
QMutexLocker locker(&m_mutex);
if (m_stop) break;
}
msleep(10);
}
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qDebug()<<"From main thread: "<"Stop Thread");
Thread t;
QObject::connect(&btn, SIGNAL(clicked()), &t, SLOT(stop()));
QObject::connect(&t, SIGNAL(finished()), &a, SLOT(quit()));
t.start();
btn.show();
return a.exec();
}
输出如下:
From main thread: 0x13a8
From worker thread: 0xab8
Thread::stop called from main thread: 0x13a8
可以看到主线程执行了 Thread::stop()
尽管上面的例子很好理解,但是,在工作线程加入了事件系统和队列连接就不那么直观了。
例如,如果我们想在工作线程中加入一些周期性的东西要怎么办?
codes
#include
class Thread : public QThread
{
Q_OBJECT
private slots:
void onTimeout()
{
qDebug()<<"Thread::onTimeout get called from? : "<private:
void run()
{
qDebug()<<"From worker thread: "<this, SLOT(onTimeout()));
timer.start(1000);
exec();
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"From main thread: "<return a.exec();
}
粗略看上去,好像并没有什么不妥,当线程开始执行,我们设置了一个定时器,连接onTimeout()
和槽函数。但是我们能够得到期望的结果吗?
事实上,结果如下:
From main thread: 0x13a4
From worker thread: 0x1330
Thread::onTimeout get called from?: 0x13a4
Thread::onTimeout get called from?: 0x13a4
Thread::onTimeout get called from?: 0x13a4
可以看得出,定时执行的操作是在主线程中执行的。很有趣,不是吗?
我们会在后续继续讨论这个执行结果的原因。
为了让才函数在工作线程中工作,有些人通过Qt::DirectConnection
去连接
connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()), Qt::DirectConnection);
另一些使用者使用如下方法:
moveToThread(this)
恩,这两个都能得到我们想要的结果。但是第二种方法是错的!!!!!!!!
即使可以得到期望结果。这是很混乱的!这不是QThread的涉及用法。QThread里所有的函数只能被创建线程所调用,而不能在启动线程使用!
事实上,根据上述观点,第一种工作方案也是错的,Thread object的成员函数onTimeout() 也被创建函数调用了。
全部用错了,怎么办?手动黑人脸.gif
由于QThread object 没有涉及能够从工作线程点用的成员。所以我们想使用slot,就必须传建一个独立的工作对象。
#include
class Worker : public QObject
{
Q_OBJECT
private slots:
void onTimeout()
{
qDebug()<<"Worker::onTimeout get called from?: "<public QThread
{
Q_OBJECT
private:
void run()
{
qDebug()<<"From work thread: "<1000);
exec();
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"From main thread: "<return a.exec();
}
运行结果如下:
From main thread: 0x810
From work thread: 0xfac
Worker::onTimeout get called from?: 0xfac
Worker::onTimeout get called from?: 0xfac
Worker::onTimeout get called from?: 0xfac
问题解决!
虽然运行很完美,但是,我们可以及注意到工作线程中仍然使用了时间循环 QThread::exec(),其他的与QThread 自身没什么关系了。
因此,因此,我们是否可以将对象创建从QThread::run()中移出,同时,它们的槽仍将被QThread调用::运行?
QThread::exec()默认被QThread::run() 调用,如果我们仅仅想要使用他,我们没必要再继承QThread 了
codes:
include <QtCore>
class Worker : public QObject { Q_OBJECT private slots: void onTimeout() { qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId(); } }; #include "main.moc" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug()<<"From main thread: "<<QThread::currentThreadId(); QThread t; QTimer timer; Worker worker; QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout())); timer.start(1000); timer.moveToThread(&t); worker.moveToThread(&t); t.start(); return a.exec(); }
运行结果如下:
From main thread: 0x1310
Worker::onTimeout get called from?: 0x121c
Worker::onTimeout get called from?: 0x121c
Worker::onTimeout get called from?: 0x121c
与所期望的一样,槽函数并没有在主线程运行。
在这个例子中, QTimer and Worker 都被放入到了子线程,事实上,把Qtimer放入子线程是不必要的。
tips:仅仅改变了QTimer
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"From main thread: "<connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));
timer.start(1000);
// timer.moveToThread(&t);
worker.moveToThread(&t);
t.start();
return a.exec();
}
与前者的不同之处在于:
在上个例子中,
- 信号timeout()在子线程中被通知
- timer和worker运行在同一个线程,他们的连接方式是直接连接(direct connection)
- 通知在同一个线程传递
然而,在本例中:
多亏这个叫做queued connections的机制,他实现了 不同线程间信号与槽函数的安全连接。如果所有的跨线程信息传递都被设计成queued connections,那么开发者就不在需要像QMutex 这样的多线程的预防措施了。
引用:
- http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-1/?utm_source=qq&utm_medium=social