Qt修炼手册11_多线程编程和QThread类

10 篇文章 0 订阅

https://blog.csdn.net/shenziheng1/article/details/60873227

1.事件循环

学习QT多线程编程之前,有必要先熟悉事件循环的概念。
先看一个单线程界面程序的主函数代码:
int main(int argc, char* argv[])
{
  QApplication app(argc,  argv);
  // 构造主窗口对象并显示
  MainWindow w;
  w.show();
  // 进入事件循环
  return app.exec();
}
在程序初始化完成后,主线程进入main()函数开始执行应用代码。一般地,我们在主线程上构建界面对象,然后进入事件循环以处理控件绘制、用户输入、系统输出等消息。这就是我们通常说的事件驱动模型。
这里需要提一个问题,为什么需要多线程编程?
多线程编程旨在提高人机交互的感受。主线程承担着用户交互的重任,当在主线程上运行费时的代码时,就会影响用户的正常操作。所以我们常把一些费时费力的计算工作移出主线程,开辟新的线程来运行之。
QThread是QT中用于线程管理的类,调用一个QThread对象的start()方法时,会创建一个新的线程并执行它的run()方法。默认地,run()会调用exec()方法进入自己的消息循环中。如下图所示:

上图中有主线程、工作线程都是执行事件循环,并且注意到主线程内部含有thr、w、objs这些QObject对象(这些对象都是在主线程上创建的)。主线程的事件循环负责检测这些对象是否有消息要处理,有的话则调用对象的slot方法。可以使用QObject::moveToThread方法将某个对象移到其他线程中,譬如:
class Worker : public QObject {
    Q_OBJECT
    …
}
 
void someFunc()
{
   QThread thr = new QThread;
   Worker worker = new Worker;
   worker->moveToThread(thr);
   thr->start();
  …
}
如果在主线程上调用someFunc(),则thr和worker在创建后关联在主线程上,当调用worker-> moveToThread()后,worker对象关联到了新的线程中,如图所示:

2.QThread类
2.1 类基础

QThread类可以不受平台影响而实现线程。QThread提供在程序中可以控制和管理现成的多种成员函数和信号/槽。通过QThread类的成员函数start()启动线程。
class Worker : public QObject
{
    Q_OBJECT
public slots:
    void doWork()
    {
        ......
    }
};
 
void MyObject::putWorkerInThread()
{
    Worker *worker = new Worker;
    QThread *workerThread = new QThread(this);
    
    connect(workerThread, &QThread::started, worker, &Worker::doWork);
    connect(workerThread, &QThread::finished,worker, &Worker::deleteLater);
    
    worker->moveToThread(workerThread);
    
    //开始进行事件循环,并发射信号workerThread->start();
    workerThread->start();
}
上述代码中,Worker类的槽与启动分离线程时发送的信号、线程终止时发送的信号相关联。如果我们启动了线程,就会调用Worker类的槽函数doWork()。

换言之,如果运行putWorkerInThread()的函数start(),那么就相当于发送了线程的启动信号(QThread::started),根据定义的信号-槽,程序就会调用与函数connect()相关联的Worker类的槽函数doWorker()。如果结束线程,则自动发送QThread::finished信号,发送此信号后,立即释放Worker使用过的线程内存区域。

QThread通过信号函数started()和finished()通知开始和结束,并查看线程状态。可以确认究竟是使用函数isfinished()信号终止线程,还是使用函数isRunning()启动线程。使用函数exit()和quit()可以结束线程。
2.2 多线程初步

如果使用多线程,有时需要等到所有线程终止。此时,使用函数wait()即可。线程中,使用成员函数sleep()、msleep()、usleep()可以暂停秒、毫秒及微秒单位的线程。
3.QThread使用的内存区域

QThread使用的内存区域分为线程私有区域和共享内存区域。线程内部使用的寄存器区域只能在线程内部共享。共享数据区域可以访问其他线程。
线程内部使用的寄存器和栈区域如下图所示:

线程内部共享区域虽然不能访问其他线程,但栈区域可以在线程间共享。因此,如果实现多种线程访问栈区域,需要注意互斥体,读写锁等线程的安全性。
3.多线程编程实例与解析

widget.h
#ifndef WIDGET_H
#define WIDGET_H
 
#include <QtWidgets/QWidget>
#include "ui_widget.h"
#include <QThread>
#include <QPushButton>
#include <QMutex>
class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread(int num);
private:
    bool threadStop;
    int number;
    QMutex mutex;
public:
    void stop();
protected:
    void run();
};
/
class widget : public QWidget
{
    Q_OBJECT
public:
    widget(QWidget *parent = 0);
    ~widget();
    MyThread *thread1;
    MyThread *thread2;
private slots:
    void btn_start();
    void btn_stop();
    void btn_isRunning();
    void btn_isFinished();
private:
    Ui::widgetClass ui;
};
 
#endif // WIDGET_H
widegt.cpp
#include "widget.h"
MyThread::MyThread(int num)
{    number = num;    }
 
void MyThread::stop()
{
    threadStop = true;
    qDebug("[%d] Thread stop", number);
}
 
void MyThread::run()
{
    threadStop = false;
    int i = 0;
 
    while(!threadStop)
    {
        mutex.lock();
        qDebug("[%d] MyThread %d", number, i);
        i++;
        sleep(1);
        mutex.unlock();
    }
}
 
///
widget::widget(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    thread1 = new MyThread(1);
    thread2 = new MyThread(2);
 
    QPushButton *btn_start = new QPushButton("START", this);
    btn_start->setGeometry(10, 10, 80, 40);
    QPushButton *btn_stop = new QPushButton("STOP", this);
    btn_stop->setGeometry(100, 10, 80, 40);
    QPushButton *btn_isRunning = new QPushButton("IsRunning", this);
    btn_isRunning->setGeometry(200, 10, 100, 40);
    QPushButton *btn_isFinished = new QPushButton("IsFinished", this);
    btn_isFinished->setGeometry(310, 10, 100, 40);
 
    connect(btn_start,      SIGNAL(clicked()), this, SLOT(btn_start()));
    connect(btn_stop,       SIGNAL(clicked()), this, SLOT(btn_stop()));
    connect(btn_isRunning,  SIGNAL(clicked()), this, SLOT(btn_isRunning()));
    connect(btn_isFinished, SIGNAL(clicked()), this, SLOT(btn_isFinished()));
}
 
void widget::btn_start()
{
    thread1->start();
    thread2->start();
}
 
void widget::btn_stop()
{
    thread1->stop();
    thread2->stop();
}
 
void widget::btn_isRunning()
{
    if(thread1->isRunning())
        qDebug("[1] Thread is running");
    else
        qDebug("[1] Thread is not running");
 
    if(thread2->isRunning())
        qDebug("[2] Thread is running");
    else
        qDebug("[2] Thread is not running");
}
 
void widget::btn_isFinished()
{
    if(thread1->isFinished())
        qDebug("[1] Thread Finish");
    else
        qDebug("[1] Thread not Finish");
 
    if(thread2->isFinished())
        qDebug("[2] Thread Finish");
    else
        qDebug("[2] Thread not Finish");
}
 
widget::~widget()
{}
输出结果:

代码分析:
1.QThread信号与槽
信号:终止线程实例运行发送信号(void finished)、启动线程实例发送信号(void started)、结束线程实例发送信号(void terminated)
槽:线程中止运行槽(finished)、线程启动槽(start)、线程结束槽(terminate)
2.QThread优先级
QThread通过函数setPriority()设置优先级。
4.关于QDebug的一点思考                                               

VS2010开发Qt,怎么显示qDebug信息(添加DOS窗口):

--------------------- 
作者:沈子恒 
来源:CSDN 
原文:https://blog.csdn.net/shenziheng1/article/details/60873227?utm_source=copy 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值