QThread多线程编程经典案例分析
2014-01-04 11:05
309 查看
说明:
传统的图形界面应用程序都只有一个线程执行,并且一次执行一个操作。如果用户调用一个比较耗时的操作,就会冻结界面响应。
一个解决方法是按照事件处理的思路:
调用 Void QApplication::processEvents() 或 void QApplication::processEvents ( int maxtime ) 来强迫事件循环进行,但是这种做法是有潜在风险的。
另外一个解决方法是:采用多线程。
QT QThread多线程编程的方法一直有个争议,就是Bradley T. Hughes:You’re doing it wrong
归纳为3中方法优劣问题:
方法(1):
1. 不使用事件循环。这是官方的 Manual 、example 以及相关书籍中都介绍的一种的方法。
a. 子类化 QThread
b. 重载 run 函数,run函数内有一个 while 或 for 的死循环
c. 设置一个标记为来控制死循环的退出。
方法(2):
这种方法也是Bradley T. Hughes极力批判的
a. 子类化 QThread,
b. 重载 run 使其调用 QThread::exec()
c. 并为该类定义信号和槽,这样一来,由于槽函数并不会在新开的 thread 运行,很多人为了解决这个问题在构造函数中调用 moveToThread(this);
而争论和不解正是这样的一条语句造成的。
Bradley T. Hughes 给出说明是: QThread 应该被看做是操作系统线程的接口或控制点,而不应该包含需要在新线程中运行的代码。需要运行的代码应该放到一个QObject的子类中,然后将该子类的对象moveToThread到新线程中。
方法(3):
在Qt4.3(包括)之前,run 是虚函数,必须子类化QThread来实现run函数。而从Qt4.4开始,qthreads-no-longer-abstract ,run 默认调用 QThread::exec() 。这样一来不需要子类化 QThread 了,只需要子类化一个 QObject 就够了,这正是被 Bradley T. Hughes推荐的方法。
类似于:
回到问题的本质,我们想要新开一个子线程来完成耗时的操作,有两个地方可以实现:(1)子类化QThread的run()函数里,在run()函数里执行耗时的操作。因为run()函数是子线程的入口函数,一定不和主线程在同一个线程。就是方法1介绍的。如果在QThread子类里的slot里执行耗时操作,因为slot是在主线程中执行的,所以行不通。(2)利用方法(3),子类化QObject
,如object。在object的slot里执行耗时操作。但是slot仍在主线程里执行,怎么办?QThread *thread;object.moveToThread(thread);
这样,这个QObject的slot都会在子线程里执行,达到了和主线程区分开的目的啦。
所以,方法(1)和方法(3)是正确的QT QThread多线程编程方法,方法(2)用的最多,但只是三人成虎,一种不恰当的用法
下面给出方法(1)和方法(3)的代码片段,各种思路请慢慢斟酌。
方法(1):
主线程开两个子线程,子线程A打印A,子线程B打印B,通过一个dialog控制。
测试结果:
方法(3)实例
结果:slot确实不在主线程中运行(这么简单不值得欢呼么?)
main thread: 0x1a5c
from thread slot: 0x186c其他
传统的图形界面应用程序都只有一个线程执行,并且一次执行一个操作。如果用户调用一个比较耗时的操作,就会冻结界面响应。
一个解决方法是按照事件处理的思路:
调用 Void QApplication::processEvents() 或 void QApplication::processEvents ( int maxtime ) 来强迫事件循环进行,但是这种做法是有潜在风险的。
按照QCoreApplication:processEvents()可能会引起递归,导致栈溢出崩溃的说法,当主线程在某个槽函数里正在执行processEvents时,
刚好有一个能响应此槽函数的信号发送过(肯定是其他线程发的信号), 这时就可能会发生可怕的递归, 导致栈溢出崩溃。 原因是processEvents在处理自己槽函数的事件时,又会调用到processEvents,进入到无尽的递归中。
另外一个解决方法是:采用多线程。QT QThread多线程编程的方法一直有个争议,就是Bradley T. Hughes:You’re doing it wrong
归纳为3中方法优劣问题:
方法(1):
1. 不使用事件循环。这是官方的 Manual 、example 以及相关书籍中都介绍的一种的方法。
a. 子类化 QThread
b. 重载 run 函数,run函数内有一个 while 或 for 的死循环
c. 设置一个标记为来控制死循环的退出。
方法(2):
这种方法也是Bradley T. Hughes极力批判的
a. 子类化 QThread,
b. 重载 run 使其调用 QThread::exec()
c. 并为该类定义信号和槽,这样一来,由于槽函数并不会在新开的 thread 运行,很多人为了解决这个问题在构造函数中调用 moveToThread(this);
而争论和不解正是这样的一条语句造成的。
Bradley T. Hughes 给出说明是: QThread 应该被看做是操作系统线程的接口或控制点,而不应该包含需要在新线程中运行的代码。需要运行的代码应该放到一个QObject的子类中,然后将该子类的对象moveToThread到新线程中。
方法(3):
在Qt4.3(包括)之前,run 是虚函数,必须子类化QThread来实现run函数。而从Qt4.4开始,qthreads-no-longer-abstract ,run 默认调用 QThread::exec() 。这样一来不需要子类化 QThread 了,只需要子类化一个 QObject 就够了,这正是被 Bradley T. Hughes推荐的方法。
类似于:
QThread thread; Object obj; Dummy dummy; obj.moveToThread(&thread); QObject::connect(&dummy, SIGNAL(sig()), &obj, SLOT(slot())); thread.start();
回到问题的本质,我们想要新开一个子线程来完成耗时的操作,有两个地方可以实现:(1)子类化QThread的run()函数里,在run()函数里执行耗时的操作。因为run()函数是子线程的入口函数,一定不和主线程在同一个线程。就是方法1介绍的。如果在QThread子类里的slot里执行耗时操作,因为slot是在主线程中执行的,所以行不通。(2)利用方法(3),子类化QObject
,如object。在object的slot里执行耗时操作。但是slot仍在主线程里执行,怎么办?QThread *thread;object.moveToThread(thread);
这样,这个QObject的slot都会在子线程里执行,达到了和主线程区分开的目的啦。
所以,方法(1)和方法(3)是正确的QT QThread多线程编程方法,方法(2)用的最多,但只是三人成虎,一种不恰当的用法
下面给出方法(1)和方法(3)的代码片段,各种思路请慢慢斟酌。
方法(1):
主线程开两个子线程,子线程A打印A,子线程B打印B,通过一个dialog控制。
(1)thread.h: #ifndef THREAD_H #define THREAD_H #include <QThread> class Thread : public QThread { Q_OBJECT public: Thread(); void setMessage(const QString &message); void stop(); protected: void run(); private: QString messageStr; volatile bool stopped; }; #endif (2)threaddialog.h: #ifndef THREADDIALOG_H #define THREADDIALOG_H #include <QDialog> #include "thread.h" class QPushButton; class ThreadDialog : public QDialog { Q_OBJECT public: ThreadDialog(QWidget *parent = 0); protected: void closeEvent(QCloseEvent *event); private slots: void startOrStopThreadA(); void startOrStopThreadB(); private: Thread threadA; Thread threadB; QPushButton *threadAButton; QPushButton *threadBButton; QPushButton *quitButton; }; #endif (3)thread.cpp #include <QtCore> #include <iostream> #include "thread.h" Thread::Thread() { stopped = false; } void Thread::setMessage(const QString &message) { messageStr = message; } void Thread::run() { while (!stopped) std::cerr << qPrintable(messageStr); stopped = false; std::cerr << std::endl; } void Thread::stop() { stopped = true; } (4)threaddialog.cpp #include <QtGui> #include "threaddialog.h" ThreadDialog::ThreadDialog(QWidget *parent) : QDialog(parent) { threadA.setMessage("A"); threadB.setMessage("B"); threadAButton = new QPushButton(tr("Start A")); threadBButton = new QPushButton(tr("Start B")); quitButton = new QPushButton(tr("Quit")); quitButton->setDefault(true); connect(threadAButton, SIGNAL(clicked()), this, SLOT(startOrStopThreadA())); connect(threadBButton, SIGNAL(clicked()), this, SLOT(startOrStopThreadB())); connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->addWidget(threadAButton); mainLayout->addWidget(threadBButton); mainLayout->addWidget(quitButton); setLayout(mainLayout); setWindowTitle(tr("Threads")); } void ThreadDialog::startOrStopThreadA() { if (threadA.isRunning()) { threadA.stop(); threadAButton->setText(tr("Start A")); } else { threadA.start(); threadAButton->setText(tr("Stop A")); } } void ThreadDialog::startOrStopThreadB() { if (threadB.isRunning()) { threadB.stop(); threadBButton->setText(tr("Start B")); } else { threadB.start(); threadBButton->setText(tr("Stop B")); } } void ThreadDialog::closeEvent(QCloseEvent *event) { threadA.stop(); threadB.stop(); threadA.wait(); threadB.wait(); event->accept(); } (5)main() #include <QApplication> #include "threaddialog.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); ThreadDialog dialog; dialog.show(); return app.exec(); }
测试结果:
方法(3)实例
#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(); }
结果:slot确实不在主线程中运行(这么简单不值得欢呼么?)
main thread: 0x1a5c
from thread slot: 0x186c其他
相关文章推荐
- QThread多线程编程经典案例分析(三种方法,解释了为什么使用moveToThread的根本原因,即为了避免调用QThread::exec() )
- QThread多线程编程经典案例分析
- QThread多线程编程经典案例分析
- Java多线程编程:变量共享分析(Thread)
- MSDN经典案例分析--PetShop
- C++多线程编程入门之经典实例
- Linux网络编程之TCP编程*(经典案例)
- 基于Linux的消息队列及多线程编程实现的聊天室(二)代码分析
- 知识众筹第9期 一个经典案例学会数据分析 | 开始分红报名
- Linux 系统应用编程——多线程经典问题(生产者-消费者)
- Java多线程编程:变量共享分析(Thread)
- Java多线程编程:变量共享分析(Thread)
- 阿里云资深DBA专家罗龙九:云数据库十大经典案例分析【转载】
- 网络编程_多线程改进多个客户端上传文件案例
- Hadoop经典案例Spark实现(一)——通过采集的气象数据分析每年的最高温度
- 心得5--XML编程(CRUD)--dom、sax解析案例分析
- C# 多线程编程 经典模型 哲学家进餐问题
- Java多线程编程:变量共享分析(Thread)
- 多线程-等待唤醒机制经典案例-生产者消费者