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

一、多线程程序

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.mfbz.cn/a/415598.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JVM的深入理解

1、JVM&#xff08;Java虚拟机&#xff09;&#xff1a;我们java编译时候&#xff0c;下通过把avac把.java文件转换成.class文件&#xff08;字节码文件&#xff09;&#xff0c;之后我们通过jvm把字节码文件转换成对应的cpu能识别的机器指令&#xff08;翻译官角色&#xff09…

TSINGSEE青犀AI智能分析网关V4智慧油田安全生产监管方案

一、方案背景 随着科技的不断发展&#xff0c;视频监控技术在油田行业中得到了广泛应用。为了提高油田生产的安全性和效率&#xff0c;建设一套智能视频监控平台保障安全生产显得尤为重要。本方案采用先进的视频分析技术、物联网技术、云计算技术、大数据和人工智能技术&#…

rhcsa(rh134)

shell 查看用户shell a、如下查看/etc/shells文件列出了系统上所有可用的 shell&#xff08;具体的可用的 shell 列表可能会因不同的红帽版本和配置而有所不同&#xff09; &#xff08;如下图/etc/shells文件包含/bin/tmux并不意味着tmux是一个shell。实际上&#xff0c;/etc/…

Git自动忽略dll文件的问题

检查了半天发现是sourcetreee的全局忽略文件导致&#xff0c; 从里面删除dll即可。 我是干脆直接删了全局忽略&#xff0c;太恶心了&#xff0c;如下&#xff1a; #ignore thumbnails created by windows Thumbs.db #Ignore files build by Visual Studio *.exe .vsconfig .s…

去中心化时代,品牌如何赢得确定性增长

去中心化时代下&#xff0c;品牌面临众多挑战。在如今复杂的环境下&#xff0c;有很多不确定的因素&#xff0c;流量、资本等等&#xff0c;这些都是品牌发展过程中的不确定因素&#xff0c;越是复杂的环境下&#xff0c;品牌越要保证自己核心优势&#xff0c;找到并放大我们的…

网站添加pwa操作和配置manifest.json后,没有效果排查问题

pwa技术官网&#xff1a;https://web.dev/learn/pwa 应用清单manifest.json文件字段说明&#xff1a;https://web.dev/articles/add-manifest?hlzh-cn Web App Manifest&#xff1a;Web App Manifest | MDN 当网站添加了manifest.json文件后&#xff0c;也引入到html中了&a…

安卓使用okhttpfinal下载文件,附带线程池下载使用

1.导入okhttp包 implementation cn.finalteam:okhttpfinal:2.0.7 2.单个下载 package com.example.downloading;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle; import android.util.Log; import android.view.View;import java.io.File;import c…

K8S存储卷与PV,PVC

一、前言 Kubernetes&#xff08;K8s&#xff09;中的存储卷是用于在容器之间共享数据的一种机制。存储卷可以在多个Pod之间共享数据&#xff0c;并且可以保持数据的持久性&#xff0c;即使Pod被重新调度或者删除&#xff0c;数据也不会丢失。 Kubernetes支持多种类型的存储卷…

Python编程实验五:文件的读写操作

目录 一、实验目的与要求 二、实验内容 三、主要程序清单和程序运行结果 第1题 第2题 四、实验结果分析与体会 一、实验目的与要求 &#xff08;1&#xff09;通过本次实验&#xff0c;学生应掌握与文件打开、关闭相关的函数&#xff0c;以及与读写操作相关的常用方法的…

74HC04六角逆变器介绍

74HC04系列六角逆变器的输入包括钳位二极管&#xff0c;允许使用限流电阻将输入连接到高于 VCC 的电压。该小工具可使用 2.0 至 6.0 伏的电源工作。当使用上拉电阻时&#xff0c;器件输入与标准 CMOS 输出和 LSTTL 输出兼容。 74HC04基础参数 • 输出驱动能力&#xff1a;10 …

Avalonia学习(二十七)-显示图像

其实和Avalonia没有什么关系&#xff0c;但是还是以其它承载&#xff0c;主要是生成二维码。 主要是库&#xff1a;QRCoder 另外是&#xff1a;SixLabors.ImageSharp&#xff0c;ZXing.ImageSharp.Barcode 用image控件显示bitmap即可。 运行效果

ArmV8架构

Armv8/armv9架构入门指南 — Armv8/armv9架构入门指南 v1.0 documentation 上面只是给了一个比较好的参考文档 其他内容待补充

JS进阶——深入对象

构造函数 封装是面向对象思想中比较重要的一部分&#xff0c;js面向对象可以通过构造函数实现的封装。 前面我们学过的构造函数方法很好用&#xff0c;但是 存在浪费内存的问题 原型 目标&#xff1a;能够利用原型对象实现方法共享 构造函数通过原型分配的函数是所有对象所…

linux系统-----------搭建LNMP 架构

PHP(Hypertext Preprocessor 超文本预处理器)是通用服务器端脚本编程语言&#xff0c;主要用于web开发实现动态web页面&#xff0c;也是最早实现将脚本嵌入HTML源码文档中的服务器端脚本语言之一。同时&#xff0c;php还提供了一个命令行接口&#xff0c;因此&#xff0c;其也可…

resilience4j 2.0.0版本使用要求最低JDK17(使用踩坑记录)

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容&#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟&#xff0c;一名Java开发工程师、Java领域优质创作者、CSDN博客专家、51CTO专家博主、阿里云专家博主、清华大学出版社签约作…

如何选择科技公司或者技术团队来开发软件项目呢

最近有客户问我们为什么同样软件项目不同公司报价和工期差异很大&#xff0c;我们给他解释好久才讲清楚&#xff0c;今天整理一下打算写一篇文章来总结一下&#xff0c;有需要开发朋友可以参考&#xff0c;我们下次遇到客户也可以直接转发文章给客户自己看。 我们根据我们自己报…

vue项目中使用antv X6新手教程,附demo案例讲解(可拖拽流程图、网络拓扑图)

前言&#xff1a; 之前分别做了vue2和vue3项目里的网络拓扑图功能&#xff0c;发现对antv X6的讲解博客比较少&#xff0c;最近终于得闲码一篇了&#xff01; 需求&#xff1a; 用户可以自己拖拽节点&#xff0c;节点之间可以随意连线&#xff0c;保存拓扑图数据后传给后端&…

TensorFlow 使用 Rust 指南

一、概述 TensorFlow是由 Google Brain 团队开发的强大的开源机器学习框架&#xff0c;已成为人工智能的基石。虽然传统上与 Python 等语言相关&#xff0c;但 Rust&#xff08;一种因其性能和安全性而受到重视的系统编程语言&#xff09;的出现为 TensorFlow 爱好者开辟了新的…

人工智能到深度学习:药物发现的机器智能方法(综述学习)

Artificial intelligence to deep learning: machine intelligence approach for drug discovery - PubMed (nih.gov) 人工神经网络、深度神经网络、支持向量机、分类和回归、生成对抗网络、符号学习和元学习是应用于药物设计和发现过程的算法的例子。人工智能已应用于药物设计…

定制开发一款家政小程序,应知应会

引言 在这个快节奏的现代生活中&#xff0c;人们对高效、便捷的家政服务的需求日益增加。随着社会结构的变化和职业生活的繁忙&#xff0c;许多家庭面临着时间不足、精力不济的挑战。在这种情况下&#xff0c;家政服务成为解决问题的有效途径。然而&#xff0c;传统的家政服务…