当前位置: 首页 > news >正文

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

阅读导航

  • 引言
  • 一、互斥锁
    • 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_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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.lryc.cn/news/446148.html

相关文章:

  • Java语法-类和对象(上)
  • Presto如何配置资源队列或资源组
  • 828华为云征文|使用Flexus X实例集成ES搜索引擎
  • 【设计模式-访问者模式】
  • 一元运算符(自增自减)
  • gitlab/极狐-离线包下载地址
  • C++——输入三个整数,按照由小到大的顺序输出。用指针方法处理。
  • 【Java8 重要特性】Lambda 表达式
  • word2vec--CBOW与Skip-Gram 两种模型
  • iOS六大设计原则设计模式
  • nacos 集群搭建
  • STM32快速复习(十二)FLASH闪存的读写
  • 漏洞扫描工具使用
  • C++ | Leetcode C++题解之第424题替换后的最长重复字符
  • 利士策分享,动摇时刻的自我救赎
  • 动手学深度学习(李沐)PyTorch 第 1 章 引言
  • 二叉树(二)深度遍历和广度遍历
  • 【算法——双指针】
  • Rocky Linux 9 中添加或删除某个网卡的静态路由的方法
  • 网站建设中常见的网站后台开发语言有哪几种,各自优缺点都是什么?
  • 【程序大侠传】应用内存缓步攀升,告警如影随形
  • JavaWEB概述
  • 半结构化知识抽取案例
  • Oracle Truncate和delete的区别
  • 应用层协议 --- HTTP
  • 网卡Network Interface Card
  • 9.1 Linux_I/O_基本知识
  • [Java]一、面向对象核心编程思想
  • 科研绘图系列:R语言多个AUC曲线图(multiple AUC curves)
  • JavaWeb--纯小白笔记06:使用Idea创建Web项目,Servlet生命周期,注解,中文乱码解决