十三、Qt多线程与线程安全

news2025/1/16 14:08:27

一、多线程程序

QThread类提供了管理线程的方法:
  • 一个对象管理一个线程
  • 一般从QThread继承一个自定义类,重载run函数

1、实现程序

在这里插入图片描述

(1)创建项目,基于QDialog

(2)添加类,修改基于QThread

在这里插入图片描述

#ifndef DICETHREAD_H
#define DICETHREAD_H

#include <QThread>

class DiceThread : public QThread
{
    Q_OBJECT

private:
    int m_seq = 0;
    int m_diceValue;
    bool m_Paused = true;
    bool m_stop = false;

public:
    explicit DiceThread();

    void diceBegin();
    void dicePause();
    void stopThread();

protected:
    void run() Q_DECL_OVERRIDE;

signals:
    void newValued(int seq, int diceValue);

public slots:
};

#endif // DICETHREAD_H
#include "dicethread.h"
#include <QTime>

DiceThread::DiceThread()
{

}

void DiceThread::diceBegin()
{
    m_Paused = false;
}

void DiceThread::dicePause()
{
    m_Paused = true;
}

void DiceThread::stopThread()
{
    m_stop = true;
}

void DiceThread::run()
{
    m_stop = false;
    m_seq = 0;
    qsrand(QTime::currentTime().second());
    while (!m_stop) {
        if(!m_Paused)
        {
            m_diceValue = qrand()%6+1;
            m_seq++;
            emit newValued(m_seq, m_diceValue);
        }
        sleep(1);
    }
    quit();
}

(3)实现按钮功能

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

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

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

    connect(&threadA, SIGNAL(started()),
            this, SLOT(on_threadAStarted()));
    connect(&threadA, SIGNAL(finished()),
            this, SLOT(on_threadAFinished()));
    connect(&threadA, SIGNAL(newValued(int,int)),
            this, SLOT(on_threadAnewValue(int,int)));
}

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

void Dialog::closeEvent(QCloseEvent *event)
{
    if(threadA.isRunning())
    {
        threadA.stopThread();
        threadA.wait();
    }
    event->accept();
}

void Dialog::on_btnStartThread_clicked()
{
    threadA.start();
}

void Dialog::on_btnStart_clicked()
{
    threadA.diceBegin();
}

void Dialog::on_btnStop_clicked()
{
    threadA.dicePause();
}

void Dialog::on_btnStopThread_clicked()
{
    threadA.stopThread();
}

void Dialog::on_btnClearText_clicked()
{
    ui->plainTextEdit->clear();
}

void Dialog::on_threadAnewValue(int seq, int diceValue)
{
    ui->plainTextEdit->appendPlainText(QString::asprintf("第%d次投色子: 点数%d", seq, diceValue));
}

void Dialog::on_threadAStarted()
{
    ui->labelStatus->setText("Thread状态:started");
    ui->btnStartThread->setEnabled(false);
    ui->btnStart->setEnabled(true);
    ui->btnStop->setEnabled(true);
    ui->btnStopThread->setEnabled(true);
}

void Dialog::on_threadAFinished()
{
    ui->labelStatus->setText("Thread状态:finished");
    ui->btnStartThread->setEnabled(true);
    ui->btnStart->setEnabled(false);
    ui->btnStop->setEnabled(false);
    ui->btnStopThread->setEnabled(false);
}

在这里插入图片描述

二、互斥量

QMutex和QMutexLocker是基于互斥量的线程同步类
  • QMutex定义的实力是互斥量,主要提供了三个函数
    • lock():锁定互斥量,如果另一个线程锁定了这个互斥量,将阻塞直到另一个解锁
    • unlock():解锁一个互斥量
    • trylock():尝试锁定一个互斥量,如果成功返回true,失败(其他线程已经锁定这个互斥量)返回false,不阻塞线程。
  • QMutexLocker简化了互斥量的处理
    • 构造一个函数接受一个互斥量作为参数,并将其锁定
    • 析构函数解锁该互斥量

实现程序

(1)拷贝上一个项目

(2)修改程序为直接读取

void DiceThread::readValue(int *seq, int *diceValue)
{
    *seq = m_seq;
    *diceValue = m_diceValue;
}

void DiceThread::run()
{
    m_stop = false;
    m_seq = 0;
    qsrand(QTime::currentTime().second());
    while (!m_stop) {
        if(!m_Paused)
        {
            m_diceValue = 50;
            msleep(50);
            m_diceValue = qrand();
            msleep(50);
            m_diceValue = m_diceValue%6+1;
            msleep(50);
            m_seq++;
//            emit newValued(m_seq, m_diceValue);
        }
        sleep(1);
    }
    quit();
}
void Dialog::on_TimerOut()
{
    int seq, diceValue;
    threadA.readValue(&seq, &diceValue);
    ui->plainTextEdit->appendPlainText(QString::asprintf("第%d次投色子: 点数%d", seq, diceValue));
}

在这里插入图片描述

(3)使用QMutex互斥量

void DiceThread::readValue(int *seq, int *diceValue)
{
    mMutex.lock();
    *seq = m_seq;
    *diceValue = m_diceValue;
    mMutex.unlock();
}

void DiceThread::run()
{
    m_stop = false;
    m_seq = 0;
    qsrand(QTime::currentTime().second());
    while (!m_stop)
    {
        if(!m_Paused)
        {
            mMutex.lock();
            m_diceValue = 50;
            msleep(50);
            m_diceValue = qrand();
            msleep(50);
            m_diceValue = m_diceValue % 6 + 1;
            msleep(50);
            m_seq++;
            //            emit newValued(m_seq, m_diceValue);
            mMutex.unlock();
        }
        sleep(1);
    }
    quit();
}

在这里插入图片描述

(4)使用QMutexLocker

void DiceThread::readValue(int *seq, int *diceValue)
{
    QMutexLocker locker(&mMutex);
    *seq = m_seq;
    *diceValue = m_diceValue;
}

(5)使用QMutex.trylock

bool DiceThread::readValue(int *seq, int *diceValue)
{
    //    QMutexLocker locker(&mMutex);
    if(mMutex.tryLock())
    {
        *seq = m_seq;
        *diceValue = m_diceValue;
        mMutex.unlock();
        return true;
    }
    return false;
}

三、读写锁

QReadWriteLock提供了以下主要函数:
  • lockForRead():只读方式锁定资源,如果有其他线程以写入方式锁定,这个函数会阻塞
  • lockForWrite():以写入方式锁定资源,如果本线程或者其他线程以读取或写入锁定资源,则函数阻塞
  • unlock():解锁
  • tryLockForRead():是lockForRead非阻塞版本
  • tryLockForWrite():是lockForWrite非阻塞版本
  • 读写锁同样有QReadLocker和QWriteLocker

四、条件变量QWaitCondition

QWaitCondition用于通知其他线程,如接收数据和处理数据之间通知。提供了一些函数:
  • wait(QMutex *lockedMutex):进入等待状态,解锁互斥量lockMutex,被唤醒后锁定lockMutex并退出函数
  • wakeAll():唤醒所有处于等待的线程,线程唤醒的顺序不确定,有操作系统调度策略决定
  • QakeOne():唤醒一个处于等待状态的线程,唤醒哪个线程不确定,由操作系统调度策略决定

1、实现程序

在这里插入图片描述

(1)拷贝上一个项目

(2)使用QWaitCondition设置数据更新

#include "dicethread.h"
#include <QTime>
#include <QWaitCondition>
#include <QMutex>

int m_seq = 0;
int m_diceValue;
bool m_stop = false;
QMutex m_Mutex;
QWaitCondition waitCondition;

ProducerThread::ProducerThread()
{

}

void ProducerThread::stopThread()
{
    m_stop = true;
}

void ProducerThread::run()
{
    m_stop = false;
    m_seq = 0;
    qsrand(QTime::currentTime().second());
    while (!m_stop)
    {
        m_Mutex.lock();
        m_diceValue = qrand() % 6 + 1;
        m_seq++;
        m_Mutex.unlock();
        waitCondition.wakeOne();
        sleep(1);
    }
    quit();
}

ConsumerThread::ConsumerThread()
{

}

void ConsumerThread::stopThread()
{
    m_stop = true;
    waitCondition.wakeOne(); // 需要给wait置信号,否则阻塞无法结束
}

void ConsumerThread::run()
{
    m_stop = false;
    while (!m_stop)
    {
        m_Mutex.lock();
        waitCondition.wait(&m_Mutex);
        emit newValued(m_seq, m_diceValue);
        m_Mutex.unlock();
        msleep(100);
    }
    quit();
}

在这里插入图片描述

五、信号量

QSemaphore信号量通常用于保护一定数量的相同的资源。QSemaphore是实现信号量功能的类,提供了以下函数:
  • acquire(int n):尝试获得n个资源,如果不够将阻塞线程,直到n个资源可用
  • release(int n):释放资源,如果资源已经全部可用,则可扩充资源总数
  • int available():返回房前信号量的资源个数
  • bool tryAcquire(int n=1):尝试获取n个资源,不成功是,不阻塞线程

1、实现程序

在这里插入图片描述

(1)创建项目,基于QDIalog

在这里插入图片描述

(2)创建线程类

(3)使用信号量实现功能

#include "threadtest.h"
#include <QSemaphore>

const int bufferSize = 8;
int buffer1[bufferSize] = {0};
int buffer2[bufferSize] = {0};
int curBuf = 1; // 当前采集数据使用的缓冲区

QSemaphore semEmptyBufs(2); // 两个资源
QSemaphore semFullBufs;

ThreadDAQ::ThreadDAQ()
{

}

void ThreadDAQ::stopThread()
{
    m_stop = true;
}

void ThreadDAQ::run()
{
    m_stop = false;
    int counter = 0;
    while(!m_stop)
    {
        semEmptyBufs.acquire();
        for (int i = 0; i < bufferSize; ++i)
        {
            if(curBuf == 1)
            {
                buffer1[i] = counter;
            }
            else
            {
                buffer2[i] = counter;
            }
            counter++;
            msleep(50);
        }
        if(curBuf == 1)
        {
            curBuf = 2;
        }
        else
        {
            curBuf = 1;
        }
        semFullBufs.release();
    }
    exit();
}

ThreadShow::ThreadShow()
{

}

void ThreadShow::stopThread()
{
    m_stop = true;
}

void ThreadShow::run()
{
    m_stop = false;
    int seq = 0;
    while(!m_stop)
    {
        semFullBufs.acquire();
        int buf[bufferSize] = {0};
        if(curBuf == 1)
        {
            memcpy(buf, buffer2, sizeof(int)*bufferSize);
        }
        else
        {
            memcpy(buf, buffer1, sizeof(int)*bufferSize);
        }
        emit newValue(buf, bufferSize, seq++);
        semEmptyBufs.release();
    }
    exit();
}

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


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

    ui->btnStopThread->setEnabled(false);

    connect(&threadConsumer, SIGNAL(newValue(int*, int, int)),
            this, SLOT(on_threadNewValue(int*, int, int)));

    connect(&threadProducer, SIGNAL(started()),
            this, SLOT(on_threadProducer_started()));
    connect(&threadProducer, SIGNAL(finished()),
            this, SLOT(on_threadProducer_finished()));
    connect(&threadConsumer, SIGNAL(started()),
            this, SLOT(on_threadConsumer_started()));
    connect(&threadConsumer, SIGNAL(finished()),
            this, SLOT(on_threadConsumer_finished()));
}

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

void Dialog::on_threadNewValue(int *data, int count, int seq)
{
    QString str = QString::asprintf("第%03d次,内容:", seq);
    for (int var = 0; var < count; ++var)
    {
        str += QString::asprintf("%03d ,", data[var]);
    }

    ui->plainTextEdit->appendPlainText(str);
}

void Dialog::on_btnStartThread_clicked()
{
    threadConsumer.start();
    threadProducer.start();

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

void Dialog::on_btnStopThread_clicked()
{
    threadProducer.stopThread();
    threadConsumer.stopThread();

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

void Dialog::on_btnClearText_clicked()
{
    ui->plainTextEdit->clear();
}

void Dialog::on_threadProducer_started()
{
    ui->labelProducer->setText("Producer线程:started");
}

void Dialog::on_threadProducer_finished()
{
    ui->labelProducer->setText("Producer线程:finished");
}

void Dialog::on_threadConsumer_started()
{
    ui->labelConsumer->setText("Consumer线程:started");
}

void Dialog::on_threadConsumer_finished()
{
    ui->labelConsumer->setText("Consumer线程:finished");
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1476129.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络-网络互连和互联网(四)

1.TCP协议&#xff1a; 传输控制协议&#xff0c;面向字节流按顺序连接&#xff0c;可靠&#xff0c;全双工&#xff0c;可变滑动窗口&#xff0c;缓冲累积传送。协议号为6。下面是TCP段&#xff08;段头&#xff09;&#xff0c;TCP头&#xff08;传输头&#xff09;&#xf…

数学建模【神经网络】

一、神经网络简介 机器学习与神经网络 机器学习是一类实现人工智能的方法总称&#xff0c;让计算机模拟或实现人类的学习行为神经网络是实现机器学习的一种模型实现机器学习的模型还有支持向量机&#xff0c;决策树&#xff0c;朴素贝叶斯分类器等 神经网络能用来做什么 已…

Lyra游戏框架宏观框架梳理

想象一下一个完整游戏流程。 Loading界面。 进入场景。 弹出Menu菜单 加载角色。 角色动画系统-切换动画系统&#xff08;默认-剑客-拳手&#xff09; 角色皮肤切换 相机 角色可以接收到用户输入&#xff0c;然后进行前后左右移动。 角色可以接收到用户输入&#xff0c;…

C语言----联合体

不知道大家是否听说过联合体这个名词。但其实大家不用觉得联合体有多特殊&#xff0c;大家可以想象结构体是一栋楼&#xff0c;里面有很多房间&#xff0c;住了形形色色的住户&#xff08;不用或者相同的数据&#xff09;。但联合体只有一个房间&#xff0c;所有的住户都挤在这…

半小时到秒级,京东零售定时任务优化怎么做的?

导言&#xff1a; 京东零售技术团队通过真实线上案例总结了针对海量数据批处理任务的一些通用优化方法&#xff0c;除了供大家借鉴参考之外&#xff0c;也更希望通过这篇文章呼吁大家在平时开发程序时能够更加注意程序的性能和所消耗的资源&#xff0c;避免在流量突增时给系统…

认证模式~

认证方式 基于Cookie和Session的认证方式 基于Cookie和Session的认证是传统的Web应用认证机制。它依赖于HTTP协议无状态的特性&#xff0c;在客户端&#xff08;浏览器&#xff09;和服务器之间保持用户的状态。 工作原理 用户登录&#xff1a;用户通过输入用户名和密码来登…

【AIGC】OpenAI推出王炸级模型sora,颠覆AI视频行业

文章目录 强烈推荐前言什么是OpenAI Sora&#xff1f;工作原理&#xff1a;算法原理&#xff1a;应用场景展望与其他视频生成模型相比有哪些优势和不足&#xff1f;优点缺点 总结强烈推荐专栏集锦写在最后 强烈推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易…

Unity中字符串拼接0GC方案

本文主要分析C#字符串拼接产生GC的原因&#xff0c;以及介绍名为ZString的库&#xff0c;它可以将字符串生成的内存分配为零。 在C#中&#xff0c;字符串拼接通常有三种方式&#xff1a; 直接使用号连接&#xff1b;string.format;使用StringBuilder&#xff1b; 下面分别细…

table展示子级踩坑

##elemenui中table通过row中是否有children进行判断是否展示子集&#xff0c;通过设置tree-prop的属性进行设置&#xff0c;子级的children的名字可以根据自己的子级名字进行替换&#xff0c;当然同样可以对数据处理成含有chilren的子级list。 问题&#xff1a; 1.如果是根据后…

java大数据开发面试题,完美世界java面试题

02 JVM 线程JVM内存区域JVM运行时内存垃圾回收与算法JAVA四种引用类型GC分代收集算法 VS 分区收集算法GC垃圾收集器JAVA IO/NIOJVM类加载器 03 JAVA集合 接口继承关系和实现LISTSETMAP 04 JAVA多线程并发 JAVA并发知识库JAVA线程实现/创建方式4种线程池线程生命周期&#xf…

★【递归】【链表】Leetcode 21. 合并两个有序链表

★【递归】【链表】Leetcode 21. 合并两个有序链表 解法1 &#xff1a;递归链表 简直是好题啊好题多做做 ---------------&#x1f388;&#x1f388;题目链接&#x1f388;&#x1f388;------------------- 解法1 &#xff1a;递归链表 简直是好题啊好题多做做 >>>…

Time Travel

题目链接 解题思路 由于所有边集中的边加起来的总和至多为&#xff0c;无向图即&#xff0c;可以存下所以直接对所有边集中的边进行建边&#xff0c;同时对于每条边&#xff0c;记录其所在边集号对于每个边集&#xff0c;由大到小维护其能通过的时间点然后从1号跑最短路到当前…

javaWeb个人学习03

事务管理: 概述: 一个事务里面的操作 要么同时成功, 要么同时失败例子: 比如在根据id 删除部门的时候 当部门删除成功了 但是遇到了异常 导致下面的代码没有继续执行下去 就没法根据id删除员工的信息了 这个时候 事务就很重要了 开启回滚 或者提交事务 要么同时成功 要么同时…

【考研数学】《汤家凤 1800 》《张宇 1000 》《李永乐 660 》《李林 880 》应该如何选择?

本人数学逆袭的路上&#xff0c;深知选对一本题集对我的重要性&#xff01;&#xff01;&#xff01; 我本科期间&#xff0c;数学并不是我的强项&#xff0c;但是我却能够在考研的时候靠数学甩开别人几十分成功上岸&#xff0c;一本优秀的题集起到了关键的作用。 1800题&…

MySQL的事务与隔离级别

1. 什么是事务&#xff1f; 数据库中的事务是指对数据库执行一批操作&#xff0c;而这些操作最终要么全部执行成功&#xff0c;要么全部失败&#xff0c;不会存在部分成功的情况。这个时候就需要用到事务。 最经典的例子就是转账&#xff0c;你要给朋友小白转 1000 块钱&…

选择排序,冒泡排序,插入排序,快速排序及其优化

目录 1 选择排序 1.1 原理 1.2 具体步骤 1.3 代码实现 1.4 优化 2 冒泡排序 2.1 原理 2.2 具体步骤 2.3 代码实现 2.4 优化 3 插入排序 3.1 原理 3.2 具体步骤 3.3 代码实现 3.4 优化 4. 快速排序 4.1 原理 4.2 具体步骤 4.3 代码实现 4.4 优化 为了讲…

如何优化一个看似正常的数据库

通常DBA是不会太了解业务逻辑的&#xff0c;遇到系统中劣质的sql 一般也是以通过添加索引的方式来优化&#xff0c;但是并不是所有的sql都能通过添加索引来优化 这就需要重sql的本身来做分析&#xff0c;另外还要了解什么样的语句会不走索引&#xff01;本文通过几个简单的例子…

RK3568 android11 调试陀螺仪模块 MPU-6500

一&#xff0c;MPU6500功能介绍 1.简介 MPU6500是一款由TDK生产的运动/惯性传感器&#xff0c;属于惯性测量设备&#xff08;IMU&#xff09;的一种。MPU6500集成了3轴加速度计、3轴陀螺仪和一个板载数字运动处理器&#xff08;DMP&#xff09;&#xff0c;能够提供6轴的运动…

计算机网络——IPV4数字报

1. IPv4数据报的结构 本结构遵循的是RFC 791规范&#xff0c;介绍了一个IPv4数据包头部的不同字段。 1.1 IPv4头部 a. 版本&#xff08;Version&#xff09;&#xff1a;指明了IP协议的版本&#xff0c;IPv4表示为4。 b. 头部长度&#xff08;IHL, Internet Header Length&…

web组态软件

1、强大的画面显示web组态功能 2、良好的开放性。 开放性是指组态软件能与多种通信协议互联&#xff0c;支持多种硬件设备&#xff0c;向上能与管理层通信&#xff0c;实现上位机和下位机的双向通信。 3、丰富的功能模块。 web组态提供丰富的控制功能库&#xff0c;满足用户的测…