QThread多线程编程经典案例分析

说明:

传统的图形界面应用程序都只有一个线程执行,并且一次执行一个操作。如果用户调用一个比较耗时的操作,就会冻结界面响应。

一个解决方法是按照事件处理的思路:

调用 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其他

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
QThread 多线程失效的原因可能有以下几个: 1. 在使用 QThread 进行多线程编程时,最常见的错误是直接继承 QThread 类并重写 run() 函数。这种方式实际上并不会在新的线程中执行 run() 函数,而是在主线程中执行。正确的做法是将需要在新线程中执行的代码放在一个 QObject 的子类中,通过将该子类的对象移动到 QThread 对象中,然后启动 QThread。 2. 如果没有在 QThread 对象上调用 start() 方法,线程将不会启动。 3. 另一个常见的错误是在主线程中直接调用 QThread 的 run() 方法。这样做会导致 run() 方法在主线程中执行,而不是在新的线程中执行。正确的做法是在主线程中调用 start() 方法,让 QThread 在新的线程中执行 run() 方法。 4. 在多线程编程中,需要注意资源的竞争和同步问题。如果多个线程同时访问共享资源,可能会导致数据不一致或死锁等问题。需要使用互斥锁或其他同步机制来保护共享资源的访问。 5. 线程的生命周期管理也是一个容易出错的地方。如果没有正确地管理线程的生命周期,可能会导致线程提前终止或无法终止等问题。需要确保在线程完成任务后正确地终止线程,并及时释放相关资源。 总之,QThread 多线程失效的原因通常是由于对 QThread 的使用不正确或对多线程编程的细节不了解所导致的。正确理解和使用 QThread,以及注意多线程编程中的同步和资源管理问题,可以避免这些问题的发生。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值