C++ Qt 创建多线程,控制6组ABB输出到UI界面上,使用moveToThread方式,配合mutex,condition_variable实现,含优化代码

目录

小项目需求:

分析:

实现:

输出A的槽:

输出B的槽:

优化逻辑:

最终跑起来效果如下:

完整代码也贴出来吧,这样大家好参考一下:


本篇内容主要涉及到的相关技术有:多线程,多线程创建的方式 moveToThread,互斥锁 mutex,条件变量 condition_variable,唯一锁 unique_lock 等。

小项目需求:

        在Qt程序中有2条线程,可分别输出A、B字符,每条线程只能启动一次,想办法控制输出顺序为:ABBABBABBABBABBABB共六组,输出到Ui界面上,用标签来显示即可

分析:

        创建线程倒是不难,使用Qt来创建的话,可以考虑使用moveToThread的方式来实现,这个项目主要考察的是如何控制2条线程的输出顺序,观察字符的规律可以发现,每3个字符就是一个组合,3个一组,这样的话,就能拿到3这个特殊的数字,共6组数,有6个A,12个B,所以一开始可以很直白的创建2个槽函数,来分别输出A或者B就可以初步效果了,加上互斥锁和条件变量的配合,使用一个字符计数器来全局统计一下,将计数器对3取余,判断余数就可以轻松实现效果,当能除尽时,余数为0,可以输出A,当余数为1或者2时,输出B,最后可以将要输出的内容通过信号的方式发给主界面即可,只需要在主界面里面再增加一个槽函数来处理这个信号即可。

实现:

两个先简单输出的槽,就可以轻松实现出来了:

全局使用3个变量,方便操作:

int g_cnt=0;
mutex g_mtx;
condition_variable g_conv;

输出A的槽:

void MyThread::outA()
{
    for(int i=0;i<6;++i){
        unique_lock<mutex> ul(g_mtx);
        g_conv.wait(ul,[=]{return g_cnt % 3 == 0;});
        qDebug()<<"A";
        g_cnt++;
        g_conv.notify_all();
    }
}

输出B的槽:

void MyThread::outB()
{
    for(int i=0;i<6*2;++i){
        unique_lock<mutex> ul(g_mtx);
        g_conv.wait(ul,[=]{return g_cnt % 3 != 0;});
        qDebug()<<"B";
        g_cnt++;
        g_conv.notify_all();
    }
}

写完这2个核心的线程槽函数之后,发现代码居然如此雷同,重复率太高了,那是不是可以考虑优化一下代码?

答案是肯定的,必须优化,要不然此代码婶婶可忍,叔叔不可忍呀;

优化逻辑:

可以考虑把需要优化的地方给提炼出来,使用一个变量来取代,那这样就可以优化2个地方:

for(int i=0;i<6;++i)   // 需要优化的第一点,把i的结束条件换成可变的

for(int i=0;i<6*2;++i) // 用另一个变量取代

for(int i=0;i<m_end;++i)  // 优化后的for循环

OK,for循环的结束条件优化完成,接着往下就得考虑把输出A或者B的地方,用一个字符串变量来替换即可

qDebug()<<"A"; // 输出A

qDebug()<<"B"; // 输出B
qDebug()<<m_str;  // 优化后的代码,用变量取代字符

还差一个最关键的地方,还得优化一下,等待条件也得优化一下

可以考虑把2个条件,换成类似的等价式即可

g_conv.wait(ul,[=]{return g_cnt % 3 == 0;});  // A的条件

g_conv.wait(ul,[=]{return g_cnt % 3 != 0;});  // B的条件

换成等价式:都换成双等于,并且只要满足其中一个条件就可以通过

g_conv.wait(ul,[=]{return g_cnt % 3 == m_first || g_cnt % 3 == m_second;});

最终,优化好的代码如下:

头文件需要添加的成员变量:

private:
    int m_end; // 结束条件
    QString m_str; // 要输出的字符
    int m_first; // 条件1
    int m_second; // 条件2

 函数实现:

void MyThread::outAB()
{
    for(int i=0;i<m_end;++i){
        unique_lock<mutex> ul(g_mtx);
        g_conv.wait(ul,[=]{return g_cnt % 3 == m_first || g_cnt % 3 == m_second;});
        qDebug()<<m_str;
        g_cnt++;
        g_conv.notify_all();
    }
}

OK,代码写到此时,基本上核心的顺序控制就已经没啥问题了,剩下的就是线程通信的问题了,得把结果输出到界面的标签上,还得给线程加上信号来传递一下字符串。

在头文件中加一个信号的声明:

signals:
    void sendABSignal(QString); // 发送字符串的信号

然后将该信号在线程槽中发射即可。

只需要在主界面类中加一个槽函数来接收处理即可:

头文件中的声明:

private slots:
    void showABSlot(QString str); // 处理AB字符到标签的槽声明

 源文件中的实现:

void ABBMainWindow::showABSlot(QString str)
{
    ui->label->setText(ui->label->text()+str); // 添加到标签中
}

最终跑起来效果如下:

完整代码也贴出来吧,这样大家好参考一下:

如果不想拷贝,也可以直接下载打包好的代码:

点我下载


线程类头文件:mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include <mutex>
#include <condition_variable>
using namespace std;

class MyThread : public QObject
{
    Q_OBJECT
public:
    explicit MyThread(int end=6,QString str="A",int first=0,int second=1,QObject *parent = nullptr);

signals:
    void sendABSignal(QString); // 发送字符串的信号

public slots:
    void outA(); // 控制输出A的槽
    void outB(); // 控制输出B的槽
    void outAB(); // 优化后的可兼容输出AB的槽
private:
    int m_end; // 结束条件
    QString m_str; // 要输出的字符
    int m_first; // 条件1
    int m_second; // 条件2
};

#endif // MYTHREAD_H

线程类源文件:mythread.cpp

#include "mythread.h"
#include <QDebug>

int g_cnt=0;
mutex g_mtx;
condition_variable g_conv;

MyThread::MyThread(int end,QString str,int first,int second,QObject *parent) : QObject(parent)
{
    m_end = end;
    m_str = str;
    m_first = first;
    m_second = second;
}

void MyThread::outA()
{
    for(int i=0;i<6;++i){
        unique_lock<mutex> ul(g_mtx);
        g_conv.wait(ul,[=]{return g_cnt % 3 == 0;});
        qDebug()<<"A";
        emit sendABSignal("A");
        g_cnt++;
        g_conv.notify_all();
    }
}

void MyThread::outB()
{
    for(int i=0;i<6*2;++i){
        unique_lock<mutex> ul(g_mtx);
        g_conv.wait(ul,[=]{return g_cnt % 3 != 0;});
        qDebug()<<"B";
        emit sendABSignal("B");
        g_cnt++;
        g_conv.notify_all();
    }
}

void MyThread::outAB()
{
    for(int i=0;i<m_end;++i){
        unique_lock<mutex> ul(g_mtx);
        g_conv.wait(ul,[=]{return g_cnt % 3 == m_first || g_cnt % 3 == m_second;});
        qDebug()<<m_str;
        emit sendABSignal(m_str);
        g_cnt++;
        g_conv.notify_all();
    }
}

主界面类头文件:

#ifndef ABBMAINWINDOW_H
#define ABBMAINWINDOW_H

#include <QMainWindow>
#include "mythread.h"
#include <QThread>

namespace Ui {
class ABBMainWindow;
}

class ABBMainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void showABSlot(QString str); // 处理AB字符到标签的槽声明

private:
    Ui::ABBMainWindow *ui;
    MyThread *m_a;
    QThread *m_aThread;

    MyThread *m_b;
    QThread *m_bThread;
};

#endif // ABBMAINWINDOW_H

主界面类源文件:

#include "abbmainwindow.h"
#include "ui_abbmainwindow.h"

ABBMainWindow::ABBMainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::ABBMainWindow)
{
    ui->setupUi(this);

    // 初始化第一组对象
    m_a = new MyThread(6,"A",0,0);
    m_aThread = new QThread;

    // 初始化第二组对象
    m_b = new MyThread(12,"B",1,2);
    m_bThread = new QThread;

    // 使用moveToThread来处理线程的操作
    m_a->moveToThread(m_aThread);
    m_b->moveToThread(m_bThread);

    // 优化前的槽关联
//    connect(m_aThread,SIGNAL(started()),m_a,SLOT(outA())); // 最开始单个函数
//    connect(m_bThread,SIGNAL(started()),m_b,SLOT(outB())); // 最开始单个函数

    // 处理多线程的关联:优化后
    connect(m_aThread,SIGNAL(started()),m_a,SLOT(outAB())); // 使用同一个槽 outAB 兼容2种字符
    connect(m_bThread,SIGNAL(started()),m_b,SLOT(outAB()));

    // 处理字符串到界面的关联
    connect(m_a,SIGNAL(sendABSignal(QString)),this,SLOT(showABSlot(QString)));
    connect(m_b,SIGNAL(sendABSignal(QString)),this,SLOT(showABSlot(QString)));

    // 启动线程
    m_aThread->start();
    m_bThread->start();
}

ABBMainWindow::~ABBMainWindow()
{
    delete ui;
    delete m_a;
    delete m_b;
    m_aThread->deleteLater();
    m_bThread->deleteLater();
}

void ABBMainWindow::showABSlot(QString str)
{
    ui->label->setText(ui->label->text()+str); // 添加到标签中
}

主函数源文件:

#include "abbmainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ABBMainWindow w;
    w.show();

    return a.exec();
}

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五木大大

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值