13-1_Qt 5.9 C++开发指南_多线程及QThread 创建多线程程序_ThreadSignal

这篇具有很好参考价值的文章主要介绍了13-1_Qt 5.9 C++开发指南_多线程及QThread 创建多线程程序_ThreadSignal。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一个应用程序一般只有一个线程,一个线程内的操作是顺序执行的,如果有某个比较消耗时间的计算或操作,比如网络通信中的文件传输,在一个线程内操作时,用户界面就可能会冻结而不能及时响应。这种情况下,可以创建一个单独的线程来执行比较消耗时间的操作,并与主线程之间处理好同步与数据交互,这就是多线程应用程序

Qt 为多线程操作提供了完整的支持。QThread 是线程类,是实现多线程操作的核心类,一般从QThread 继承定义自己的线程类。

线程之间的同步是其交互的主要问题,Qt 提供了 QMutex、QMutexLocker、QReadWriteLock、QwaitCondition、QSemaphore 等多种类用于实现线程之间的同步

Qt 还有 Qt Concurrent 模块,提供一些高级的 API 实现多线程编程而无需使用 QMutex、QwaitCondition 和QSemaphore 等基础操作。使用Qt Concurrent 实现的多线程程序可以自动根据处理器内核个数调整线程个数。

1. QThread 类功能简介

QThread会起一个子线程,并可以通过信号槽将变量传递到主线程中。

QThread 类提供不依赖于平台的管理线程的方法。一个 QThread 类的对象管理一个线程,一般从QThread 继承一个自定义类,并重定义虚函数run(),在run()函数里实现线程需要完成的任务。

将应用程序的线程称为主线程,额外创建的线程称为工作线程。一般在主线程里创建工作线程,并调用 start()开始执行工作线程的任务。start()会在内部调用 run()函数,进入工作线程的事件循环,在 run()函数里调用 exit()或 quit()可以结束线程的事件循环,或在主线程里调用 terminate()强制结束线程。

QThread 类的主要接口函数、信号和槽函数见下表。

13-1_Qt 5.9 C++开发指南_多线程及QThread 创建多线程程序_ThreadSignal,# Qt 5.9 C++开发指南,qt,c++
13-1_Qt 5.9 C++开发指南_多线程及QThread 创建多线程程序_ThreadSignal,# Qt 5.9 C++开发指南,qt,c++
QThread 是 QObject 的子类,所以可以使用信号与槽机制。QThread 自身定义了 started()和finished()两个信号,started()信号在线程开始执行之前发射,也就是在 run()函数被调用之前,finished0信号在线程就要结束时发射。

在进行本章的学习前,建议先复习C++新特性中对应部分,Qt这里就是对C++11的语法进行封装。

2. 掷骰子的线程QDiceThread

作为实例,定义一个掷骰子的线程类QDiceThread,类的声明部分如下:

#ifndef QDICETHREAD_H
#define QDICETHREAD_H

#include    <QThread>

class QDiceThread : public QThread
{
    Q_OBJECT
private:
    int     m_seq=0;//掷骰子次数序号
    int     m_diceValue;//骰子点数
    bool    m_Paused=true; //掷一次骰子
    bool    m_stop=false; //停止线程
protected:
    void    run() Q_DECL_OVERRIDE;  //线程任务
public:
    QDiceThread();

    void    diceBegin();//掷一次骰子
    void    dicePause();//暂停
    void    stopThread(); //结束线程
signals:
    void    newValue(int seq,int diceValue); //产生新点数的信号
};

#endif // QDICETHREAD_H

重载虚函数 run(),在此函数里完成线程的主要任务。

自定义 diceBegin()、dicePause()、stopThread()3 个公共函数用于线程控制,这3 个函数由主线程调用。

定义了一个信号 newValue(int seq,int diceValue) 用于在掷一次子得到新的点数之后发射此信号,由主线程的槽函数响应以获取值。
QDiceThread 类的实现代码如下:

#include "qdicethread.h"
#include    <QTime>

QDiceThread::QDiceThread()
{

}

void QDiceThread::diceBegin()
{ //开始掷骰子
    m_Paused=false;
}

void QDiceThread::dicePause()
{//暂停掷骰子
    m_Paused=true;
}

void QDiceThread::stopThread()
{//停止线程
    m_stop=true;
}

void QDiceThread::run()
{//线程任务
    m_stop=false;//启动线程时令m_stop=false
    m_seq=0; //掷骰子次数
    qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的

    while(!m_stop)//循环主体
    {
        if (!m_Paused)
        {
            m_diceValue=qrand(); //获取随机数
            m_diceValue=(m_diceValue % 6)+1;
            m_seq++;
            emit newValue(m_seq,m_diceValue);  //发射信号
        }
        msleep(500); //线程休眠500ms
    }

//  在  m_stop==true时结束线程任务
    quit();//相当于  exit(0),退出线程的事件循环
}

其中,run()是线程任务的实现部分,线程开始就执行 run()函数。run()函数一般是事件循环过程,根据各种条件或事件处理各种任务。当run()函数退出时,线程的事件循环就结束了。

在run()函数里,初始化变量 m_stop 和m_seq,用qsrand()函数对随机数种子初始化。run()函数的主循环体是一个 while循环,在主线程调用 stopThread()函数使 m_stop 为 true,才会退出 while循环,调用quit()之后结束线程。

在 while 循环体内,又根据 m_Paused 判断当前是否需要掷子,如果需要掷骰子,则用随机函数生成一次子的点数 m_diceValue,然后发射信号 newValue(),将 m seq和m diceValue作为信号参数传递出去。主线程可以设计槽函数与此信号关联,获取这两个值并进行显示。

3. 掷骰子的多线程应用程序

使用QDiceThread 类,设计一个应用程序 samp13_1,程序运行界面如下图所示。
13-1_Qt 5.9 C++开发指南_多线程及QThread 创建多线程程序_ThreadSignal,# Qt 5.9 C++开发指南,qt,c++
窗体上方的几个按钮用于控制线程的启动与停止,控制开始与暂停掷骰子。中间的文本框显示次数和点数,右边根据点数显示资源文件里面的一个图片,图片存储在项目的资源文件里。下方的一个标签根据QDiceThread 的 started()和finished()两个信号显示线程的状态。

3.1 可视化UI设计

13-1_Qt 5.9 C++开发指南_多线程及QThread 创建多线程程序_ThreadSignal,# Qt 5.9 C++开发指南,qt,c++

3.2 代码分析

窗口类是从QDialog 继承的类 Dialog,其类定义如下(省略了按钮槽函数的定义):

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>

#include    "qdicethread.h"

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

private:
    QDiceThread   threadA;

protected:
    void    closeEvent(QCloseEvent *event);

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private slots:
...

private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H

这里定义了一个QDiceThread 类型的变量 threadA,重定义了 closeEvent()事件,自定义了3个槽函数。
Dialog类的构造函数代码如下:

Dialog::Dialog(QWidget *parent) : QDialog(parent),  ui(new Ui::Dialog)
{//构造函数
    ui->setupUi(this);
    connect(&threadA,SIGNAL(started()),this,SLOT(onthreadA_started()));
    connect(&threadA,SIGNAL(finished()),this,SLOT(onthreadA_finished()));

    connect(&threadA,SIGNAL(newValue(int,int)),this,SLOT(onthreadA_newValue(int,int)));
}

构造函数主要是将 threadA 的 3 个信号与 Dialog 自定义的3 个槽函数相关联,这3 个槽函数的代码如下:

void Dialog::onthreadA_started()
{//线程的started()信号的响应槽函数
    ui->LabA->setText("Thread状态:thread started");
}

void Dialog::onthreadA_finished()
{//线程的 finished()信号的响应槽函数
    ui->LabA->setText("Thread状态:thread finished");
}

void Dialog::onthreadA_newValue(int seq,int diceValue)
{//QDiceThread的newValue()信号的响应槽函数,显示骰子次数和点数
    QString  str=QString::asprintf("第 %d 次掷骰子,点数为:%d",seq,diceValue);
    ui->plainTextEdit->appendPlainText(str);

    QPixmap pic; //图片显示
    QString filename=QString::asprintf(":/dice/images/d%d.jpg",diceValue);
    pic.load(filename);
    ui->LabPic->setPixmap(pic);
}

started()信号发射时,表示线程开始执行,在标签里显示状态文字。

finished()信号发射时,表示线程结束执行,在标签里显示状态文字。

newValue()是 QDiceThread 定义的信号,在掷一次骰子获得新的点数后发射,将掷假子的次数和点数传递过来。槽函数onthreadA_newValue()获取这两个值并显示在文本框里,再根据点数从资源文件里获取相应的图片并显示。

窗口上5个按钮的代码如下:

void Dialog::on_btnStartThread_clicked()
{//启动线程 按钮
    threadA.start();

    ui->btnStartThread->setEnabled(false);
    ui->btnStopThread->setEnabled(true);

    ui->btnDiceBegin->setEnabled(true);
    ui->btnDiceEnd->setEnabled(false);
}

void Dialog::on_btnDiceBegin_clicked()
{//开始 掷骰子按钮
    threadA.diceBegin();
    ui->btnDiceBegin->setEnabled(false);
    ui->btnDiceEnd->setEnabled(true);
}

void Dialog::on_btnDiceEnd_clicked()
{//暂停 掷骰子按钮
    threadA.dicePause();
    ui->btnDiceBegin->setEnabled(true);
    ui->btnDiceEnd->setEnabled(false);
}

void Dialog::on_btnStopThread_clicked()
{//结束线程 按钮
    threadA.stopThread();//结束线程的run()函数执行
    threadA.wait();//

    ui->btnStartThread->setEnabled(true);
    ui->btnStopThread->setEnabled(false);

    ui->btnDiceBegin->setEnabled(false);
    ui->btnDiceEnd->setEnabled(false);
}

void Dialog::on_btnClear_clicked()
{ //清空文本 按钮
    ui->plainTextEdit->clear();
}

“启动线程”按钮调用线程的 start()函数,start()函数会内部调用 run()函数开始线程任务的执行。run()函数将内部变量 m_Paused 初始化为true,所以,启动线程后并不会立即开始掷散子。

“开始”按钮调用 diceBegin()函数,使 threadA 线程内部变量 m_Paused 变为 false,那么run()函数里就开始每隔 500 毫秒产生一次骰子点数,并发射信号 newValue()。
“暂停”按钮调用 dicePause()函数,使 threadA 线程内部变量 m_Paused 变为 true,run()函数里不再掷骰子,但是 run()函数并没有结束,也就是线程并没有结束。
“结束线程”按钮调用 stopThread()函数,使threadA 线程内部的 m_stop 变为 true,run()函数体的 while 循环结束,执行 quit()后线程结束。所以,线程结束就是 run()函数执行退出。

重载closeEvent()事件,在窗口关闭时确保线程被停止,代码如下:

void Dialog::closeEvent(QCloseEvent *event)
{ //窗口关闭事件,必须结束线程
    if (threadA.isRunning())
    {
        threadA.stopThread();
        threadA.wait();
    }
    event->accept();
}

3.3 程序结构及源码

3.3.1 程序结构

13-1_Qt 5.9 C++开发指南_多线程及QThread 创建多线程程序_ThreadSignal,# Qt 5.9 C++开发指南,qt,c++

3.3.2 qdicethread.h

#ifndef QDICETHREAD_H
#define QDICETHREAD_H

#include    <QThread>

class QDiceThread : public QThread
{
    Q_OBJECT
private:
    int     m_seq=0;//掷骰子次数序号
    int     m_diceValue;//骰子点数
    bool    m_Paused=true; //掷一次骰子
    bool    m_stop=false; //停止线程
protected:
    void    run() Q_DECL_OVERRIDE;  //线程任务
public:
    QDiceThread();

    void    diceBegin();//掷一次骰子
    void    dicePause();//暂停
    void    stopThread(); //结束线程
signals:
    void    newValue(int seq,int diceValue); //产生新点数的信号
};

#endif // QDICETHREAD_H

3.3.3 qdicethread.cpp

#include "qdicethread.h"
#include    <QTime>

QDiceThread::QDiceThread()
{

}

void QDiceThread::diceBegin()
{ //开始掷骰子
    m_Paused=false;
}

void QDiceThread::dicePause()
{//暂停掷骰子
    m_Paused=true;
}

void QDiceThread::stopThread()
{//停止线程
    m_stop=true;
}

void QDiceThread::run()
{//线程任务
    m_stop=false;//启动线程时令m_stop=false
    m_seq=0; //掷骰子次数
    qsrand(QTime::currentTime().msec());//随机数初始化,qsrand是线程安全的

    while(!m_stop)//循环主体
    {
        if (!m_Paused)
        {
            m_diceValue=qrand(); //获取随机数
            m_diceValue=(m_diceValue % 6)+1;
            m_seq++;
            emit newValue(m_seq,m_diceValue);  //发射信号
        }
        msleep(500); //线程休眠500ms
    }

//  在  m_stop==true时结束线程任务
    quit();//相当于  exit(0),退出线程的事件循环
}


3.3.4 dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>

#include    "qdicethread.h"

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

private:
    QDiceThread   threadA;

protected:
    void    closeEvent(QCloseEvent *event);

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private slots:
    void    onthreadA_started();
    void    onthreadA_finished();
    void    onthreadA_newValue(int seq, int diceValue);

    void on_btnClear_clicked();

    void on_btnDiceEnd_clicked();

    void on_btnDiceBegin_clicked();

    void on_btnStopThread_clicked();

    void on_btnStartThread_clicked();

private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H

3.3.5 dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

void Dialog::closeEvent(QCloseEvent *event)
{ //窗口关闭事件,必须结束线程
    if (threadA.isRunning())
    {
        threadA.stopThread();
        threadA.wait();
    }
    event->accept();
}

Dialog::Dialog(QWidget *parent) : QDialog(parent),  ui(new Ui::Dialog)
{//构造函数
    ui->setupUi(this);
    connect(&threadA,SIGNAL(started()),this,SLOT(onthreadA_started()));
    connect(&threadA,SIGNAL(finished()),this,SLOT(onthreadA_finished()));

    connect(&threadA,SIGNAL(newValue(int,int)),this,SLOT(onthreadA_newValue(int,int)));
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::onthreadA_started()
{//线程的started()信号的响应槽函数
    ui->LabA->setText("Thread状态:thread started");
}

void Dialog::onthreadA_finished()
{//线程的 finished()信号的响应槽函数
    ui->LabA->setText("Thread状态:thread finished");
}

void Dialog::onthreadA_newValue(int seq,int diceValue)
{//QDiceThread的newValue()信号的响应槽函数,显示骰子次数和点数
    QString  str=QString::asprintf("第 %d 次掷骰子,点数为:%d",seq,diceValue);
    ui->plainTextEdit->appendPlainText(str);

    QPixmap pic; //图片显示
    QString filename=QString::asprintf(":/dice/images/d%d.jpg",diceValue);
    pic.load(filename);
    ui->LabPic->setPixmap(pic);
}

void Dialog::on_btnClear_clicked()
{ //清空文本 按钮
    ui->plainTextEdit->clear();
}

void Dialog::on_btnDiceEnd_clicked()
{//暂停 掷骰子按钮
    threadA.dicePause();
    ui->btnDiceBegin->setEnabled(true);
    ui->btnDiceEnd->setEnabled(false);
}

void Dialog::on_btnDiceBegin_clicked()
{//开始 掷骰子按钮
    threadA.diceBegin();
    ui->btnDiceBegin->setEnabled(false);
    ui->btnDiceEnd->setEnabled(true);
}

void Dialog::on_btnStopThread_clicked()
{//结束线程 按钮
    threadA.stopThread();//结束线程的run()函数执行
    threadA.wait();//

    ui->btnStartThread->setEnabled(true);
    ui->btnStopThread->setEnabled(false);

    ui->btnDiceBegin->setEnabled(false);
    ui->btnDiceEnd->setEnabled(false);
}

void Dialog::on_btnStartThread_clicked()
{//启动线程 按钮
    threadA.start();

    ui->btnStartThread->setEnabled(false);
    ui->btnStopThread->setEnabled(true);

    ui->btnDiceBegin->setEnabled(true);
    ui->btnDiceEnd->setEnabled(false);
}

详细可见附带源码文件文章来源地址https://www.toymoban.com/news/detail-622351.html

到了这里,关于13-1_Qt 5.9 C++开发指南_多线程及QThread 创建多线程程序_ThreadSignal的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 16-1_Qt 5.9 C++开发指南_多语言界面

    有些软件需要开发多语言界面版本,如中文版和英文版,并且在软件里可以方便地切换界面语言。Qt 为多语言界面提供了很好的支持,使用 Qt 的一些规则和工具,可以很方便地为应用程序开发提供多语言界面支持。 用 Qt 开发多语言界面应用程序,主要包括以下几个步骤。

    2024年02月14日
    浏览(33)
  • 04-2_Qt 5.9 C++开发指南_SpinBox使用

    QSpinBox 用于整数的显示和输入,一般显示十进制数,也可以显示二进制、十六进制的数,而且可以在显示框中增加前缀或后缀。 QDoubleSpinBox 用于浮点数的显示和输入,可以设置显示小数位数,也可以设置显示的前缀或后缀。 实例samp4_3 演示QSpinBox和QDoubleSpinBox这两个组件的使

    2024年02月14日
    浏览(49)
  • 15-1_Qt 5.9 C++开发指南_Qt多媒体模块概述

    多媒体功能指的主要是计算机的音频和视频的输入、输出、显示和播放等功能,Qt 的多媒体模块为音频和视频播放、录音、摄像头拍照和录像等提供支持,甚至还提供数字收音机的支持。本章将介绍 Qt 多媒体模块的功能和使用。 Qt 多媒体模块提供了很多类,可以实现如下的

    2024年02月13日
    浏览(37)
  • 16-4_Qt 5.9 C++开发指南_Qt 应用程序的发布

    用 Qt 开发一个应用程序后,将应用程序提供给用户在其他计算机上使用就是应用程序的发布。应用程序发布一般会提供一个安装程序,将应用程序的可执行文件及需要的运行库安装到用户计算机上,即使用户计算机上没有安装 Qt 也能正常运行安装的程序。 Qt的应用程序发布

    2024年02月14日
    浏览(40)
  • 04-5_Qt 5.9 C++开发指南_QComboBox和QPlainTextEdit

    QComboBox 是下拉列表框组件类,它提供一个下拉列表供用户选择,也可以直接当作一个QLineEdit 用作输入。OComboBox 除了显示可见下拉列表外,每个项 (item,或称列表项)还可以关联一个 QVariant 类型的变量,用于存储一些不可见数据。 QPlainTextEdit 是一个多行文本编辑器,用于显示

    2024年02月14日
    浏览(42)
  • 04-6_Qt 5.9 C++开发指南_QListWidget和QToolButton

    Qt 中用于项 (Item)处理的组件有两类, 一类是 Item Views ,包括 QListView、QTreeView、QTableView、QColumnView 等; 另一类是 Item Widgets ,包括 QListWidget、QTreeWidget 和QTableWidget。 Item Views 基于模型/视图(Model/Vicw)结构,视图 (View)与模型数据(Model Data)关联实现数据的显示和编辑,模型/视图结

    2024年02月13日
    浏览(37)
  • 08-3_Qt 5.9 C++开发指南_Graphics View绘图架构

    采用QPainter 绘图时需要在绘图设备的 paintEvent()事件里编写绘图的程序,实现整个绘图过程。这种方法如同使用 Windows 的画图软件在绘图,绘制的图形是位图,这种方法适合于绘制复杂性不高的固定图形,不能实现图件的选择、编辑、拖放、修改等功能。 Qt 为绘制复杂的可交

    2024年02月13日
    浏览(30)
  • 04-4_Qt 5.9 C++开发指南_时间日期与定时器

    时间日期是经常遇到的数据类型,Qt 中时间日期类型的类如下。 QTime:时间数据类型,仅表示时间,如 15:23:13。 QDate:日期数据类型,仅表示日期,如2017-4-5. QDateTime:日期时间数据类型,表示日期和时间,如2017-03-23 08:12:43. Qt 中有专门用于日期、时间编辑和显示的 界面组件 ,介

    2024年02月14日
    浏览(38)
  • 06-2_Qt 5.9 C++开发指南_自定义对话框及其调用

    本篇介绍到的对话框及其调用实例较为复杂但十分详细,如果做了解可以先参考:QT从入门到实战x篇_13_模态和非模态对话框创建。 在一个应用程序设计中,为了实现一些特定的功能,必须设计自定义对话框。自定义对话框的设计一般从QDialog 继承,并且可以采用 UI 设计器可

    2024年02月13日
    浏览(33)
  • 14-5_Qt 5.9 C++开发指南_基于HTTP 协议的网络应用程序

    Qt 网络模块提供一些类实现 OSI 7 层网络模型中高层的网络协议,如 HTTP、FTP、SNMP等,这些类主要是 QNetworkRequest、QNetworkReply和QNetworkAccessManager。 QNetworkRequest 类通过一个URL 地址发起网络协议请求,也保存网络请求的信息,目前支持 HTTP、FTP 和局部文件 URLs的下载或上传。 QNe

    2024年02月13日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包