Qt (19)【Qt 线程安全 | 互斥锁QMutex QMutexLocker | 条件变量 | 信号量】

news2024/9/21 19:59:57

阅读导航

  • 引言
  • 一、互斥锁
    • 1. QMutex
      • (1)基本概念
      • (2)使用示例
        • 基本需求
        • ⭕thread.h
        • ⭕thread.cpp
        • ⭕widget.h
        • ⭕widget.cpp
    • 2. QMutexLocker
      • (1)基本概念
      • (2)使用示例
    • 3. QReadWriteLocker、QReadLocker、QWriteLocker
      • (1)基本概念
      • (2)使用示例
  • 二、条件变量
    • 1. 基本概念
    • 2. 使用示例
  • 三、信号量
    • 1. 基本概念
    • 2. 使用示例

引言

在Qt中,为了构建高效且安全的多线程应用程序,我们需要关注线程安全问题。Qt提供了几种关键的同步机制,包括互斥锁(QMutex)及其自动管理助手QMutexLocker,用于确保资源互斥访问;条件变量,用于线程间的协调与等待/唤醒机制;以及信号量,用于控制对资源的并发访问数。这些工具共同协作,帮助开发者避免数据竞争和死锁,实现线程间的安全交互。
(PS:Linux有详细讲过这方面的知识,传送门:💻【探索Linux】P.21多线程 | 线程同步 | 条件变量 | 线程安全)

一、互斥锁

互斥锁是⼀种保护和防止多个线程同时访问同⼀对象实例的方法,在Qt中,互斥锁主要是通过QMutex类来处理

1. QMutex

(1)基本概念

QMutex 是 Qt 框架中一个重要的类,它实现了互斥锁的功能。主要用途是在多线程程序中保护对共享资源的访问,通过锁定和解锁机制来确保同一时间只有一个线程能够访问特定的数据或执行某段代码,从而避免数据竞争和不一致的问题,实现线程间的互斥操作,保证程序的线程安全性

(2)使用示例

基本需求

使用两个线程对同一个全局变量进行加加,每个线程加5000次

⭕thread.h
#ifndef THREAD_H
#define THREAD_H

#include <QWidget>
#include <QThread>
#include <QMutex>

class Thread : public QThread
{
    Q_OBJECT
public:
    Thread();

    // 添加一个 static 成员.
    static int num;
    // 创建锁对象
    static QMutex mutex;

    void run();
};

#endif // THREAD_H

⭕thread.cpp
#include "thread.h"
#include <QMutexLocker>

int Thread::num = 0;

QMutex Thread::mutex;

Thread::Thread()
{
}

void Thread::run()
{
    for (int i = 0; i < 50000; i++) {
        mutex.lock();
        num++;
        mutex.unlock();
    }
}

⭕widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

⭕widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "thread.h"
#include <QDebug>

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

    // 创建两个线程对象.
    Thread t1;
    Thread t2;
    t1.start();
    t2.start();

    // 加上线程的等待, 让主线程等待这俩线程执行结束.
    t1.wait();
    t2.wait();

    // 打印结果.
    qDebug() << Thread::num;
}

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

2. QMutexLocker

(1)基本概念

QMutexLockerQMutex 的一个辅助类,它采用了资源获取即初始化(RAII, Resource Acquisition Is Initialization)的设计模式。这一模式确保了互斥锁(QMutex)在 QMutexLocker 对象的生命周期内被自动上锁,并在该对象被销毁时自动解锁。通过这种方式,QMutexLocker 极大地简化了对互斥锁的管理,开发者无需手动调用上锁(lock)和解锁(unlock)函数,从而避免了因忘记解锁而导致的死锁等潜在问题。

(2)使用示例

在这里插入图片描述
上面的QMutex的示例代码只用进行图片中的变换就可以使用QMutexLocker了。

3. QReadWriteLocker、QReadLocker、QWriteLocker

(1)基本概念

QReadWriteLock 是一个读写锁类,它设计用于精细控制对共享资源的并发访问权限,特别是在处理读写操作时。在这个锁机制中,区分了读操作和写操作的不同需求。

  • QReadLocker 是用于读操作的上锁工具。当多个线程需要同时读取共享资源时,QReadLocker 允许这些线程同时获得读锁,从而并行访问资源。这种方式提高了读取操作的并发效率,因为读取通常不会修改数据,因此多个读者可以同时安全地访问资源。

  • QWriteLocker 则是用于写操作的上锁工具。与读操作不同,写操作会修改共享资源,因此 QWriteLocker 确保在任何给定时间内,只有一个线程能够持有写锁,从而独占对共享资源的写入权限。这保证了数据的一致性和完整性,避免了并发写入可能导致的冲突。

(2)使用示例

	QReadWriteLock rwLock;
	
	//在读操作中使⽤读锁
	{
		QReadLocker locker(&rwLock); //在作⽤域内⾃动上读锁
		//读取共享资源
		//...
	} //在作⽤域结束时⾃动解读锁
	
	//在写操作中使⽤写锁
	{
		QWriteLocker locker(&rwLock); //在作⽤域内⾃动上写锁
		//修改共享资源
		//...
	} //在作⽤域结束时⾃动解写锁

二、条件变量

1. 基本概念

在多线程编程中,有时线程需要等待某个条件成立才能继续执行。Qt提供了QWaitCondition类来处理这种情况。QWaitCondition允许线程在条件不满足时暂停(等待),并在条件满足时被其他线程唤醒。这样,线程可以基于条件进行同步和协调,而不仅仅是简单地轮流使用资源。

2. 使用示例

QMutex mutex;
QWaitCondition condition;

//在等待线程中
mutex.lock();

//检查条件是否满⾜,若不满⾜则等待

while (!conditionFullfilled())
{
	condition.wait(&mutex); //等待条件满⾜并释放锁
}

//条件满⾜后继续执⾏
//...

mutex.unlock();

//在改变条件的线程中
mutex.lock();

//改变条件
changeCondition();
condition.wakeAll(); //唤醒等待的线程

mutex.unlock();

三、信号量

1. 基本概念

在多线程编程中,如果多个线程需要访问有限的资源(如内存),就需要控制同时访问这些资源的线程数量。Qt中的QSemaphore就是一个工具,它可以限制同时运行的线程数,从而管理资源的使用。简单来说,QSemaphore就像一个计数器,用于确保不会有太多线程同时占用资源。

2. 使用示例

QSemaphore semaphore(2); //同时允许两个线程访问共享资源

//在需要访问共享资源的线程中
semaphore.acquire(); //尝试获取信号量,若已满则阻塞

//访问共享资源
//...

semaphore.release(); //释放信号量

//在另⼀个线程中进⾏类似操作

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

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

相关文章

AI论文写作PPT思维导图PC小程序开发

AI论文写作PPT思维导图PC小程序开发 AI智能PPT功能 一键生成PPT大纲、一键扩写大纲内容、单独扩写某个大纲内容、一键生成内容关键词、单项内容关键词生成、新增大纲项、修改大纲、删除大纲、选择PPT模板、单页模板一键切换、在线编辑模板&#xff1b;支持导出PPTX、JPEG、&am…

nodejs 013:Prect 样式复用(multiple classes)例子

Prect 简单示例 Prect 为使用相同的现代 API 的快速 3kB React 替代方案。代码形式与 React 基本相同。部分语法区别可见 prect-differences-to-react。以下是一个 Prect 简单示例。 Button目录Button.css&#xff1a; .this {display: inline-block;padding: 3px 8px;margi…

执行网络攻击模拟的 7 个步骤

在进攻和防守策略方面&#xff0c;我们可以从足球队和美式足球队身上学到很多东西。球员们会分析对方球队的策略&#xff0c;找出弱点&#xff0c;相应地调整进攻策略&#xff0c;最重要的是&#xff0c;练习、练习、再练习。作为最低要求&#xff0c;网络安全部门也应该这样做…

基于微信的设备故障报修管理系统设计与实现+ssm论文源码调试讲解

2相关技术 2.1微信小程序 小程序是一种新的开放能力&#xff0c;开发者可以快速地开发一个小程序。小程序可以在微信内被便捷地获取和传播&#xff0c;同时具有出色的使用体验。尤其拥抱微信生态圈&#xff0c;让微信小程序更加的如虎添翼&#xff0c;发展迅猛。 2.2 MYSQL数据…

一文彻底搞懂大模型 - OpenAI o1(最强推理模型)

最近这一两周看到不少互联网公司都已经开始秋招提前批面试了。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c;帮助一些…

举例说明:自然语言处理实战项目

自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是人工智能领域的一个重要分支&#xff0c;旨在使计算机能够理解、解释和生成人类语言。以下是一些NLP实战项目的示例&#xff1a; 1. 情感分析&#xff08;Sentiment Analysis&#xff09; 项目描述: …

用 HTML + JavaScript DIY 一个渐进式延迟法定退休年龄测算器

为减轻社会和个人因退休年龄变化带来的冲击&#xff0c;近日&#xff0c;全国人民代表大会常务委员会正式发布了关于实施渐进式延迟法定退休年龄的重要决定。 根据该决定&#xff0c;我国将同步启动对男、女职工法定退休年龄的延迟计划。这一调整将采取渐进式的方式进行&#…

09年408考研真题-数据结构

数据结构 10.【2009统考真题】为解决计算机主机与打印机之间速度不匹配的问题&#xff0c;通常设置一个打印数据缓冲区&#xff0c;主机将要输出的数据依次写入该缓冲区&#xff0c;而打印机则依次从该缓冲区中取出数据。该缓冲区的逻辑结构应该是(B&#xff09;。 A.栈 …

unix中如何查询和修改进程的资源限制

一、前言 一个进程在运行时&#xff0c;会用到各种资源&#xff0c;比如cpu的使用时间、内存空间、文件等等。那么&#xff0c;一个进程能够占用多少资源呢&#xff1f;cpu使用的时间有多长&#xff1f;进程空间有多大&#xff1f;能够创建多少个文件&#xff1f;这个就是本文…

数字IC设计\FPGA 职位经典笔试面试整理--基础篇1

注&#xff1a; 资料都是基于网上一些博客分享和自己学习整理而成的 1&#xff1a;什么是同步逻辑和异步逻辑&#xff1f; 同步逻辑是时钟之间有固定的因果关系。异步逻辑是各时钟之间没有固定的因果关系。 同步时序 逻辑电路的特点&#xff1a;各触发器的时钟端全部连接在一…

蚁群算法(ACO算法)求解实例---旅行商问题 (TSP)

目录 一、采用ACO求解 TSP二、 旅行商问题2.1 实际例子&#xff1a;求解 6 个城市的 TSP2.2 **求解该问题的代码&#xff0c;代码&#xff08;完整代码关注底部微信公众号获取&#xff09;**2.3 代码运行过程截屏2.4 代码运行结果截屏&#xff08;后续和其他算法进行对比&#…

Acwing数据结构:单链表

单链表 主要思想&#xff1a;使用数组实现链表(而不用结构体&#xff0c;结构体代码更长&#xff0c;后续图论也是基于数组实现&#xff09;&#xff0c;即静态链表。因为动态链表使用new申请空间需要较多的时间&#xff0c;而算法要求的是以较少的时间完成任务。 单链表&…

软件测试八股文

Part1 1、你的测试职业发展是什么&#xff1f; 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前3年积累测试经验&#xff0c;按如何做好测试工程师的要点去要求自…

appimage 软件创建桌面快捷图标

1、appimage 软件创建桌面快捷图标 1&#xff09;下载 AppImage 文件 首先&#xff0c;确保你已经下载了 AppImage 文件&#xff0c;并且该文件具有可执行权限。你可以通过以下命令为 AppImage 文件添加可执行权限&#xff1a; chmod x /path/to/your-app.AppImage2&#xff…

大模型入门 ch04:实现一个GPT模型

本文是github上的大模型教程LLMs-from-scratch的学习笔记&#xff0c;教程地址&#xff1a;教程链接 LLM大模型主要是参数量大&#xff0c;而不是代码量大。 这是本节的具体内容 首先实现一个GPT的骨架分别实现GPT骨架内的各个部分&#xff0c;包括LayerNorm&#xff0c;GELU,…

shopro前端 短信登录只显示模板不能正常切换

删掉 换成下面的代码 // 打开授权弹框 export function showAuthModal(type smsLogin) {const modal $store(modal);setTimeout(() > {modal.$patch((state) > {state.auth type;});}, 100); }

docker操作的基本命令加容器的基本命令(仅供自己参考)

1、docker build&#xff1a;本地将一个docker文件打包成镜像 2、docker push&#xff1a;将自己打包的镜像传到镜像服务器上 3、docker pull&#xff1a;将镜像服务器上的镜像拉取到本地 4、docker images&#xff1a; 查看镜像服务器上的镜像 5、docker rmi&#xff1a;删…

编译成功!QT/6.7.2/Creator编译Windows64 MySQL驱动(MSVC版)

相邻你找了很多博文&#xff0c;都没有办法。现在终于找到了正宗。 参考 GitHub - thecodemonkey86/qt_mysql_driver: Typical symptom: QMYSQL driver not loaded. Solution: get pre-built Qt SQL driver plug-in required to establish a connection to MySQL / MariaDB u…

伊犁云计算22-1 raid 5 linux 配置

&#xff11;  添加四块&#xff53;&#xff41;&#xff54;&#xff41; 硬盘  &#xff12;  设置启动项为原来&#xff53;&#xff43;&#xff53;&#xff49; 的硬盘 &#xff13;  四块盘都是  &#xff46;&#xff44;   &#xff4c;&#xff49;&…

仓颉编程入门2,启动HTTP服务

上一篇配置了仓颉sdk编译和运行环境&#xff0c;读取一个配置文件&#xff0c;并把配置文件简单解析了一下。 前面读取配置文件&#xff0c;使用File.readFrom()&#xff0c;这个直接把文件全部读取出来&#xff0c;返回一个字节数组。然后又创建一个字节流&#xff0c;给文件…