qt 线程同步-互斥量(Qmutex)

2023-05-16

        在多线程应用程序中,由于多个线程的存在,线程之间可能需要访问同一个变量,或一个线程需要等待另外一个线程完成某个操作后才产生相应的动作,这时候就需要做线程同步。所以,需要线程同步情况:

        1)多个线程之间访问同一个变量;

        2)一个线程需要等待另外一个线程完成某个操作后才产生相应的动作。

问题引入:

        示例1,使用了信号与槽机制,在产生新的骰子之后使用信号通知主线程读取新数据。如果不使用信号与槽,就需要主线程进行查询。

1、基于互斥量的线程同步

        QMutex和QMutexLocker是基于互斥量的线程同步类,QMutex定义的实例是一个互斥量, QMulex主要提供3个函数。

        •lock():锁定互斥量,如果另外一个线程锁定了这个互斥量,它将阻塞执行直到其他线程解锁这个互斥量。

        •unlock():解锁一个互斥量,需要与lock()配对使用。

        •tryLock():试图锁定一个互斥量,如果成功锁定就返回true;如果其他线程已经锁定了这 个互斥量,就返回false,但不阻塞程序执行。

        定义的互斥量mutex相当于一个标牌,可以这样来理解互斥 :列车上的卫生间一次只能进 一个人,当一个人尝试进入卫生间就是lock(),如果有人占用,他就只能等待;等里面的人出来, 腾出了卫生间是unlock(),这个等待的人才可以进入并且锁住卫生间的门,就是lock(),使用完卫生间之后他再出来时就是unlock()。

        使用互斥量,对QDiceThread类重新定义,不采用信号与槽机制,而是提供一个函数用于主线程读取数据。更改后的QDiceThread类定义增加如下:

#include    <QThread>
#include    <QMutex>

class QDiceThread : public QThread
{
    Q_OBJECT
public:
    QDiceThread();
    bool    readValue(int *seq, int *diceValue);  // 用于主线程读取数据的函数
    // ...

private:
    QMutex  mutex; // 互斥量
    // ...
};

#endif // QDICE_THREAD_H

        QDiceThread类里用QMutex类定义了一个互斥量变量mutex。

        定义了函数readValue(),用于外部线程读取掷骰子的次数和点数,传递参数采用指针变量, 以便一次读取两个数据。

        下面是QDiceThread类中关键的和readValue。函数的实现代码。

void QDiceThread::run()
{
     qDebug()<< "QDiceThread run(), tid :" << QThread::currentThreadId();

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

    while(!m_stop)//循环主体
    {
        if (!m_Paused)
        {
            mutex.lock();
            m_diceValue=qrand(); //获取随机数
            m_diceValue=(m_diceValue % 6)+1;
            m_seq++;
            qDebug() <<  "produce new value:"  << m_seq << m_diceValue
                     << "tid :" << QThread::currentThreadId();;
            mutex.unlock();
        }
        msleep(500); //线程休眠500ms
    }

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

        在run()函数中,对重新计算骰子点数和掷骰子次数的3行代码用互斥量mutex的lock()和unlock()进行了保护,这部分代码的执行就不会被其他线程中断。注意lock()和unlock()必须配对使用。

        在readValue()函数中,用互斥量mutex的tryLock()和unlock()进行了保护。如果tryLock()成功锁定互斥量,读取数值的两行代码执行时不会被中断,执行完后解锁;如果tryLock 锁定失败, 函数就立即返回,而不会等待。

注意:

        原理上,对于两个或多个线程可能会同时读或写的变量应该使用互斥量进行保护,例如 QDiceThread中的变量m_stop和m_paused,在run()函数中读取这两个变量,要在diceBcgin()、diceEnd()和stopThread()函数里修改这些值,但是这3个函数都只有一条赋值语句,可以认为是原 子操作,所以,可以不用锁定保护。

        主程序中,不断调用readValue()函数不断读取数值。这里使用定时器,用于定是主动读取投掷骰子线程的数值。

主线程处理类定义:

#include <QObject>
#include <QTimer>
#include "qdice_thread.h"

class Process: public QObject
{
    Q_OBJECT
private:
    QTimer mTimer; // 定时器
    int mSeq, mDiceValue;
    // ...

public slots:
    // ...
    void onTimeOut(); //定期器处理槽函数
};
#endif // PROCESS_H

        主要是增加了一个定时器mTimer和其时间溢出响应槽函数onTimeOut(),在处理类的构造函 数中将mTimer的timeout信号与此槽函数关联。

connect(&mTimer, SIGNAL(timeout()), this, SLOT(onlimeOut()));

        onTimeOut()函数的主要功能是调用threadA的readValue()函数读取数值。定时器的定时周期设置为100ms,小于threadA产生一次新数据的周期(500ms),所以可能读出旧的数据,通过存储的掷骰子的次数。读取的掷骰子次数是否不同,判断是否为新数据。onTimeOut()函数的代码如下:

// 定时器到时处理槽函数
void Process::onTimeOut()
{
    qDebug()<< "Process onTimeOut(), tid :" << QThread::currentThreadId();
    int tmpSeq=0, tmpValue=0;
    bool valid = threadA.readValue(&tmpSeq, &tmpValue); // 读取数值
    if (valid && (tmpSeq != mSeq)) // 有效,并且是新数据
    {
        mSeq = tmpSeq;
        mDiceValue = tmpValue;
        qDebug()<< "get new value :" << "mSeq=" << mSeq << ",mDiceValue=" << mDiceValue;
    }
    // usleep(1000);
}

启动线程时添加定时器周期设置:

mSeq=0; 
mTimer.start(100); // 定时器100读取一次数据

线程结束时,增加定时器暂停:

mTimer.stop();

运行结果:

定时器消息延迟测试:

        结论:当添加比较大的延迟时,定时器消息依然按固定时间执行。

        QMuiex需要配对使用lock()和unlock()来实现代码段的保护,在一些逻辑复杂的代码段或可能发生异常的代码中,配对就可能出错。

        QMutexLocker是另外一个简化了互斥量处理的类。QMutexLocker的构造函数接受一个互斥量作为参数并将其锁定,QMulcxLockcr的析构函数则将此互斥量解锁,所以在QMutexLocker实例变最的生存期内的代码段得到保护,自动进行互斥量的锁定和解锁。例如,QDiceThread的run() 函数的代码可以改写如下:

// 定义
#include  <QMutex>
QMutex  mutex; //互斥量

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

    while(!m_stop)//循环主体
    {
        if (!m_paused)
        {
//            mutex.lock();
            QMutexLocker    Locker(&mutex);
            m_diceValue=qrand(); //获取随机数
            m_diceValue=(m_diceValue % 6)+1;
            m_seq++;
//            mutex.unlock();
        }
        msleep(500); //线程休眠100ms
    }
}

 传送门:qt多线程系列文章目录

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

qt 线程同步-互斥量(Qmutex) 的相关文章

随机推荐

  • 计算机主机的灯一直闪,电脑主机红灯一直闪正常吗

    大家好 xff0c 我是时间财富网智能客服时间君 xff0c 上述问题将由我为大家进行解答 电脑主机红灯一直闪不正常 xff0c 原因 xff1a 1 可能是你的主机箱上的灯坏了 2 中病毒了 xff0c 一直在读写你的硬盘 3 开机启动项
  • ROS wrapper for the ZED SDK (ZED相机的ROS驱动包的使用)

    前言 ZED相机 High Resolution and High Frame rate 3D Video Capture Depth Perception indoors and outdoors at up to 0 5 xff5e 2
  • 杭州电子科技大学全国计算机排名,杭电排名为什么比211还高,杭州电子科技大学是211吗...

    所谓 双非 高校 xff0c 在以往一般指的是非985和211高校 xff0c 而现在一般指非双一流 一流大学建设和一流学科建设 高校 今天笔者给大家介绍的这所高校 xff0c 就是一所典型的 双非 高校 xff0c 然而 xff0c 在往
  • 服务器主板cpld芯片作用,CPLD的工作原理是什么呢?

    CPLD 复杂可编程逻辑器件 xff0c 它是从PAL和GAL器件发展出来的器件 xff0c 相对而言规模大 xff0c 结构复杂 xff0c 属于大规模集成电路范围 它的工作方式和原理与FPGA 现场可编程门阵列 相类似 xff0c 都是
  • 服务器维护 灵魂兽,魔兽世界7.0新灵魂兽麋鹿捕捉方法

    可盘游戏BT盒子是一款功能十分强大的游戏福利软件 xff0c 这里汇聚了全网最热门最好玩的手游 大家不仅可以在这里获得各种折扣福利 xff0c 还能下载游戏 类型 xff1a 辅助工具 大小 xff1a 17 42M 语言 xff1a 简体
  • 换热站无线通讯服务器,供暖换热站的手机APP智能调控研究

    摘要 xff1a 我国目前集中供热需求量较大 每年消耗大量的煤炭 电力 根据2014年统计数据 我国集中供热能耗平均在20至25公斤标煤 m 2 而欧洲为10至15公斤标煤 m 2 1 可见 我国集中供热能耗高出同纬度甚至更高纬度的欧洲国家
  • 虚拟主机ip和服务器ip一样,虚拟主机独立ip和服务器的独立ip区别-

    虚拟从机独立ip和办事器的独立ip区别是什么 xff1f 从IP利用上来看 xff0c 虚拟从机分派的独立IP和办事器的独立IP并没无什么区别 xff0c 都是一样的拜候感化 虽然虚拟从机是从办事器上划分出来的一部门空间 xff0c 不外度
  • TX2系统镜像制作及烧写

    可以通过制作镜像的方法来快速复制板子的环境 xff0c 达到快速部署的目的 tx2系列资源总目录地址 xff1a https blog csdn net weixin 34910922 article details 106933180 P
  • tx2刷机-安装环境

    tx2系列资源总目录地址 xff1a https blog csdn net weixin 34910922 article details 106933180 1 解压 Linux Driver Package xff0c 生成文件夹目录
  • TX2下查看CPU GPU 内存使用率

    声明 xff1a 文章仅作知识整理 分享 xff0c 如有侵权请联系作者删除博文 xff0c 谢谢 xff01 tx2系列资源总目录地址 xff1a https blog csdn net weixin 34910922 article d
  • onnxsim-让导出的onnx模型更精简

    用torch导出的模型有时候参数过多 xff0c 不利于查看 查找资料onnxsim可以简化模型 xff0c 让显示更加自然 如 xff0c reshap层的导出 xff0c 红框中细节参数被显示出现 默认导出 xff1a 使用onnxsi
  • conda国内源-持续更新

    清华源 中科大源大部分时间能用 xff0c 但没事也会被封 最近发现的北外源挺好用 推荐使用 xff1a 北外镜像 xff08 推荐 xff09 xff1a conda config add channels https mirrors b
  • (三)PX4在gazebo中仿真障碍物检查与路径规划【完全SIL】

    首先完成 ROS PX4 firmware gazebo的环境配置 可以参考上一篇博客 二 PX4编译与gazebo仿真环境的搭建 ubuntu14 amp ubuntu16 安装 障碍物检测与路径规划的一些依赖库 span class h
  • python使用tcp传输图像

    使用tcp进行图像网络收发 主要分为两个部分 xff1a 1 服务端 step1 启动服务 xff0c 等待客户端连接 xff1b step2 等待并接受客户端数据 xff1b step3 接收的二进制流解码 xff0c 显示 xff1b
  • apt-get install 报错:无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系

    sudo apt get install安装软件时 xff0c 出现错误 无法修正错误 xff0c 因为您要求某些软件包保持现状 xff0c 就是它们破坏了软件包间的依赖关系 提示已经很明显了 xff0c 依赖关系问题 百度后 xff0c
  • 数据增强(Data Augmentation)常用方法汇总

    1 数据增强的作用 1 xff09 避免过拟合 当数据集具有某种明显的特征 xff0c 例如数据集中图片基本在同一个场景中拍摄 xff0c 使用Cutout方法和风格迁移变化等相关方法可避免模型学到跟目标无关的信息 2 xff09 提升模型
  • uint8_t和char的相互转换

    uint8 t 和char 的相互转换以及uint8 t 和int16 t 的相互转换 实际应用需要 xff0c 实现uint8 int16 t int32 t的按字节传输 xff0c 实现发送和接收功能 如int16 t xff0c 需要
  • c++ sleep函数头文件

    c 43 43 11标准 xff0c 提供线程休眠函数 xff1a using namespace std chrono literals std this thread sleep for 5 线程休眠5ms 1 windows下支持 x
  • shell指令自带sudo密码

    希望在sh脚本文件中的shell指令中使用sudo命令 xff0c 需要自动输入密码以确保顺序执行 有两种方式可以实现 xff1a 1 将密码以参数形式追加到脚本 使用管道 xff1a echo 密码 sudo S shell命令 如 xf
  • qt 线程同步-互斥量(Qmutex)

    在多线程应用程序中 xff0c 由于多个线程的存在 xff0c 线程之间可能需要访问同一个变量 xff0c 或一个线程需要等待另外一个线程完成某个操作后才产生相应的动作 xff0c 这时候就需要做线程同步 所以 xff0c 需要线程同步情况