转载自http://qimo601.iteye.com/blog/1271835
原文链接:http://hi.baidu.com/cyclone/blog/item/65f3f603294f2e783812bb51.html
注意:请优先考虑Qt 线程基础(QThread、QtConcurrent等)
dbzhang800 2011.06.18
本文主要内容:
在任务一中,用 四 种方式实现:点击界面按钮,开线程运行一段程序,结果显示在一个Label上。
为了简单起见,本文只讲如何做及其结果是什么,而不讲其原因是什么(估计大家对原因也不会感兴趣,详见: QThread 使用探讨 和 QThread使用方法)。
Manual 中的原话:
概念一:QThread 对象本身所依附的线程 和它管理的线程不是同一个线程。
概念二:你在QThread派生类中定义的槽是在主线程而不是在次线程中执行的。
概念三:除了Manual和Qt例子中给出的用法外,QThread有一种更容易且被推荐的使用方法:
你从代码中包含的 #include“main.moc”应该能看出
为了省几行代码,头文件都是直接包含 QtCore 和 QtGui。
main 函数中输出主线程IDqDebug()<<"main: "<<QThread::currentThreadId();
run 函数中输出次线程IDqDebug()<<"thread: "<<currentThreadId();
槽函数中输出其在哪个线程中执行qDebug()<<"slots1: "<<currentThreadId();
因为用了qDebug,所以你的pro文件内最好加上 CONFIG+=console
点击界面按钮,开线程运行一段程序,结果显示在一个Label上。
很容易想到方法,代码可以工作,结果正确。但 ... 未必和你想得一样
#include <QtCore> #include <QtGui> class Thread:public QThread { Q_OBJECT public: Thread(){} public slots: void slot1(int v) { qDebug()<<"slots1: "<<currentThreadId(); emit sig1(QString::number(v*v*v)); } signals: void sig1(const QString& t); protected: void run() { qDebug()<<"thread: "<<currentThreadId(); exec(); } }; class Widget:public QWidget { Q_OBJECT public: Widget():m_label(new QLabel), m_button(new QPushButton("Button")), m_thread(new Thread) { QVBoxLayout * vbox = new QVBoxLayout(this); vbox->addWidget(m_label); vbox->addWidget(m_button); setLayout(vbox); connect(m_button,SIGNAL(clicked()),this,SLOT(onButtonClicked())); connect(this,SIGNAL(clicked(int)),m_thread,SLOT(slot1(int))); connect(m_thread,SIGNAL(sig1(QString)),m_label,SLOT(setText(QString))); m_thread->start(); } signals: void clicked(int v); private slots: void onButtonClicked() { static int v = 0; emit clicked(v); v++; } private: QLabel * m_label; QPushButton * m_button; Thread * m_thread; }; #include "main.moc" int main(int argc, char** argv) { QApplication app(argc, argv); qDebug()<<"main: "<<QThread::currentThreadId(); Widget w; w.show(); return app.exec(); }
一切工作正常,但看看控制台输出呢?
main: 3055777552 thread: 3024481136 slots1: 3055777552 slots1: 3055777552 slots1: 3055777552 ...
这儿明确告诉你,slot1 是在主线程中执行的。
我们试试 Qt Manual和 Qt 例子中采用的解决方案。
槽函数不是在主线程运行么,而run函数不是次线程么?那么我们就:
这样以来,尽管槽函数在仍在主线程,但费时的计算代码都在次线程了。
对Thread的类的改造如下(程序其他部分和 尝试一 完全一样):
class Thread:public QThread { Q_OBJECT public: Thread(){} public slots: void slot1(int v) { qDebug()<<"slots1: "<<currentThreadId(); m_mutex.lock(); m_vals.enqueue(v); m_mutex.unlock(); } signals: void sig1(const QString& t); protected: void run() { qDebug()<<"thread: "<<currentThreadId(); while(1) { m_mutex.lock(); if (!m_vals.isEmpty()){ int v = m_vals.dequeue(); emit sig1(QString::number(v*v*v)); } m_mutex.unlock(); } } private: QQueue<int> m_vals; QMutex m_mutex; };
注意哦,因为 slot 函数在主线程中,而run函数在次线程中,所以二者需要 QMutex 实现对变量的安全访问。如果你认真看过Qt自带的例子,会发现它始终强调 QMutex 的使用。
尝试二是"正统"的做法,但如过你用Google搜索过。那么你可能不会选择尝试二,而是会使用下面的方法(其他部分和尝试一 完全一样)
class Thread:public QThread { Q_OBJECT public: Thread(){ moveToThread(this); } ...
这样以来,slot函数确实是在次线程工作的,看看控制台输出
main: 3056785168 thread: 3024444272 slots1: 3024444272 slots1: 3024444272 ...
很有意思?不是么,一条 moveToThread(this),移动到自己。然后问题解决了。
o(∩∩)o...哈哈,不要太高兴哦,这个方法看起来比较舒服,但是它是被官方人员强烈批判的用法
终于到我想写的代码了,这是Qt线程的开发者建议的使用方式,但很可惜。直到目前(Qt4.7.0),手册和例子中对此都只字为提。
class Worker:public QObject { Q_OBJECT public: Worker(){} public slots: void slot1(int v) { qDebug()<<"slots1: "<<QThread::currentThreadId(); emit sig1(QString::number(v*v*v)); } signals: void sig1(const QString& t); };
class Widget:public QWidget { Q_OBJECT public: Widget():m_label(new QLabel), m_button(new QPushButton("Button")), m_worker(new Worker) { QVBoxLayout * vbox = new QVBoxLayout(this); vbox->addWidget(m_label); vbox->addWidget(m_button); setLayout(vbox); QThread * thread = new QThread(this); m_worker->moveToThread(thread); connect(m_button,SIGNAL(clicked()),this,SLOT(onButtonClicked())); connect(this,SIGNAL(clicked(int)),m_worker,SLOT(slot1(int))); connect(m_worker,SIGNAL(sig1(QString)),m_label,SLOT(setText(QString))); thread->start(); } signals: void clicked(int v); private slots: void onButtonClicked() { static int v = 0; emit clicked(v); v++; } private: QLabel * m_label; QPushButton * m_button; Worker * m_worker; };
main 函数还是和尝试一完全一样
控制台输出结果如下
main: 3056961296 slots1: 3024616304 slots1: 3024616304 ....
一共两个线程,且二者id不同,说明slot在次线程中
恩。这篇文字似乎又不短了,看来任务二要另起一篇了。