Qt/C++ 多线程同步机制详解及应用

在多线程编程中,线程之间共享资源可能会导致数据竞争和不一致的问题。因此,采用同步机制确保线程安全至关重要。在Qt/C++中,常见的同步机制有:互斥锁(QMutexstd::mutex)、信号量(QSemaphore)、读写锁(QReadWriteLock)、原子操作(QAtomicInt 等)以及条件变量(QWaitConditionstd::condition_variable)。本文将详细介绍这些常用同步机制,并通过代码示例帮助理解其使用场景。

在这里插入图片描述

1. 互斥锁(QMutex / std::mutex

互斥锁是最常用的线程同步机制之一,它可以保证在同一时刻只有一个线程进入临界区。其基本原理是通过锁定和解锁互斥锁来确保共享资源的独占访问。

示例代码及注释
#include <QMutex>
#include <QThread>
#include <iostream>

// 声明一个全局互斥锁,用于保护共享资源
QMutex mutex;
int sharedResource = 0;  // 共享资源

class Worker : public QThread {
public:
    void run() override {
        mutex.lock();  // 锁定互斥锁,确保当前线程独占资源
        std::cout << "Thread " << QThread::currentThreadId() << " is entering the critical section." << std::endl;
        sharedResource++;  // 修改共享资源
        std::cout << "Shared Resource: " << sharedResource << std::endl;
        mutex.unlock();  // 释放互斥锁,其他线程可以进入
    }
};

int main() {
    Worker worker1, worker2;  // 创建两个工作线程
    worker1.start();  // 启动第一个线程
    worker2.start();  // 启动第二个线程
    worker1.wait();   // 等待第一个线程完成
    worker2.wait();   // 等待第二个线程完成
    return 0;
}
解释:
  1. 互斥锁机制:使用 mutex.lock() 锁定,mutex.unlock() 解锁,保证共享资源在某个线程执行完后才可被其他线程访问。
  2. 线程独占性:多个线程同时访问共享资源时,只有获取到锁的线程能进入临界区,避免竞争条件。

2. 信号量(QSemaphore

信号量是一种同步机制,用于控制多个线程同时访问有限数量的资源。与互斥锁不同,信号量可以允许多个线程并发访问,直到信号量资源用尽。

示例代码及注释
#include <QSemaphore>
#include <QThread>
#include <iostream>

// 初始化信号量,允许最多3个线程同时进入临界区
QSemaphore semaphore(3);

class Worker : public QThread {
public:
    void run() override {
        semaphore.acquire();  // 获取信号量
        std::cout << "Thread " << QThread::currentThreadId() << " is entering." << std::endl;
        QThread::sleep(1);    // 模拟执行任务的时间
        std::cout << "Thread " << QThread::currentThreadId() << " is leaving." << std::endl;
        semaphore.release();  // 释放信号量
    }
};

int main() {
    Worker worker1, worker2, worker3, worker4;  // 创建四个线程
    worker1.start();  // 启动线程
    worker2.start();
    worker3.start();
    worker4.start();  // 第四个线程需等待其他线程释放信号量
    worker1.wait();
    worker2.wait();
    worker3.wait();
    worker4.wait();
    return 0;
}
解释:
  1. 并发控制:信号量通过 acquire() 获取资源,release() 释放资源,控制多个线程并发执行。
  2. 资源计数:当信号量值为零时,其他线程必须等待信号量被释放才能继续执行。

3. 读写锁(QReadWriteLock

读写锁允许多个线程同时读取共享资源,但只有一个线程可以进行写入操作。它适合在多读少写的场景中使用,能有效提高读取操作的并发性能。

示例代码及注释
#include <QReadWriteLock>
#include <QThread>
#include <iostream>

// 读写锁,保护共享资源
QReadWriteLock lock;
int sharedResource = 0;  // 共享资源

class Reader : public QThread {
public:
    void run() override {
        lock.lockForRead();  // 获取读锁
        std::cout << "Reader thread " << QThread::currentThreadId() << " reading: " << sharedResource << std::endl;
        lock.unlock();  // 释放读锁
    }
};

class Writer : public QThread {
public:
    void run() override {
        lock.lockForWrite();  // 获取写锁
        sharedResource++;
        std::cout << "Writer thread " << QThread::currentThreadId() << " writing: " << sharedResource << std::endl;
        lock.unlock();  // 释放写锁
    }
};

int main() {
    Reader reader1, reader2;  // 创建两个读线程
    Writer writer1;  // 创建一个写线程
    reader1.start();
    reader2.start();
    writer1.start();  // 写线程将阻塞后续的读操作
    reader1.wait();
    reader2.wait();
    writer1.wait();
    return 0;
}
解释:
  1. 多读单写lock.lockForRead() 允许多个线程同时读取,而 lock.lockForWrite() 则保证写操作期间的独占访问。
  2. 适用场景:在多读少写的场景下,读写锁可以提高效率。

4. 原子操作(QAtomicInt / std::atomic

原子操作是一种无需锁定的同步方式,适合处理简单的共享数据操作,如计数器的增减。它通过硬件保证操作的原子性,从而避免了线程间的数据竞争。

示例代码及注释
#include <QAtomicInt>
#include <QThread>
#include <iostream>

// 原子整型,用于原子递增
QAtomicInt atomicCounter = 0;

class Worker : public QThread {
public:
    void run() override {
        for (int i = 0; i < 1000; ++i) {
            atomicCounter.ref();  // 原子递增操作
        }
    }
};

int main() {
    Worker worker1, worker2;  // 创建两个线程
    worker1.start();
    worker2.start();
    worker1.wait();
    worker2.wait();
    std::cout << "Final counter: " << atomicCounter.load() << std::endl;  // 输出最终的计数值
    return 0;
}
解释:
  1. 轻量同步atomicCounter.ref() 是一个原子操作,不需要使用互斥锁,适合简单的递增或递减操作。
  2. 性能提升:相比于使用锁,原子操作的性能开销更小,非常适合对共享资源进行计数的场景。

5. 条件变量(QWaitCondition / std::condition_variable

条件变量用于线程间的同步,它允许线程等待某个条件被满足,通常与互斥锁一起使用。在条件满足时,可以唤醒一个或多个等待的线程。

示例代码及注释
#include <QMutex>
#include <QWaitCondition>
#include <QThread>
#include <iostream>

// 声明互斥锁和条件变量
QMutex mutex;
QWaitCondition condition;
bool ready = false;  // 条件标志

class Worker : public QThread {
public:
    void run() override {
        mutex.lock();  // 获取互斥锁
        while (!ready) {
            condition.wait(&mutex);  // 等待条件满足,期间释放互斥锁
        }
        std::cout << "Thread " << QThread::currentThreadId() << " is processing." << std::endl;
        mutex.unlock();  // 释放互斥锁
    }
};

int main() {
    Worker worker1, worker2;  // 创建两个线程
    worker1.start();
    worker2.start();
    
    QThread::sleep(1);  // 模拟主线程准备时间
    mutex.lock();
    ready = true;
    condition.wakeAll();  // 唤醒所有等待的线程
    mutex.unlock();

    worker1.wait();
    worker2.wait();
    return 0;
}
解释:
  1. 条件等待:线程使用 condition.wait() 进入等待状态,直到条件满足时被唤醒。
  2. 唤醒机制condition.wakeAll() 唤醒所有正在等待条件的线程,使它们重新进入竞争状态。

在多线程编程中,选择合适的同步机制是确保数据安全和程序高效运行的关键。根据不同的应用场景,互斥锁、信号量、读写锁、原子操作和条件变量各有优缺点:

  • 互斥锁 适用于保护临界区,确保共享资源的独占访问;
  • 信号量 控制并发资源的访问数量;
  • 读写锁 适合多读少写的场景,提升读取性能;
  • 原子操作 在简单的共享数据操作中高效且轻量;
  • 条件变量 允许线程等待某个条件的满足,以实现灵活的线程同步。

合理使用这些工具能够有效避免多线程编程中的竞争条件,确保程序的安全和性能。

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

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

相关文章

deepin桌面版连接windows远程桌面

在Linux系统中&#xff0c;要登录到Windows系统&#xff0c;通常可以使用远程桌面协议(RDP)。你需要在Linux系统上安装RDP客户端。 使用如下命令安装rdp协议&#xff1a; sudo apt-get install xrdp 安装成功后&#xff0c;启动rdp服务。 sudo systemctl start xrdp 有了r…

【诉讼流程-健身房-违约-私教课-诉讼书提交流程-民事诉讼-自我学习-铺平通往法律的阶梯-讲解(3)】

【诉讼流程-健身房-违约-私教课-诉讼书提交流程-民事诉讼-自我学习-铺平通往法律的阶梯-讲解&#xff08;3&#xff09;】 1、前言说明2、流程说明3、现场提交&#xff08;线下&#xff09;4、网上提交1-起诉书样例2-起诉书编写&#xff08;1&#xff09;原告信息&#xff1a;&…

Vue3.0组合式API:使用reactive()、ref()创建响应式代理对象

Vue3.0组合式API系列文章&#xff1a; 《Vue3.0组合式API&#xff1a;setup()函数》 《Vue3.0组合式API&#xff1a;使用reactive()、ref()创建响应式代理对象》 《Vue3.0组合式API&#xff1a;computed计算属性、watch监听器、watchEffect高级监听器》 《Vue3.0组合式API&…

漏洞挖掘 | Selenium Grid 中的 SSRF

Selenium 网格框架上的基本服务器端请求伪造 最近&#xff0c;我正在阅读漏洞文章看到Peter Jaric写的一篇 Selenium Grid 文章&#xff1b;他解释了 Selenium Grid 框架上缺乏身份验证和安全措施强化的问题。 在网上进行了更多搜索&#xff0c;我发现 Selenium Grid 开箱即用…

音视频入门基础:FLV专题(3)——FLV header简介

一、引言 本文对FLV格式的FLV header进行简介&#xff0c;FLV文件的开头就是FLV header。 进行简介之前&#xff0c;请各位先从《音视频入门基础&#xff1a;FLV专题&#xff08;1&#xff09;——FLV官方文档下载》下载FLV的官方文档《video_file_format_spec_v10_1.pdf》和…

【第十二章:Sentosa_DSML社区版-机器学习之回归】

目录 12.1 线性回归 12.2 决策树回归 12.3 梯度提升决策树回归 12.4 保序回归 12.5 XGBoost回归 12.6 随机森林回归 12.7 广义线性回归 12.8 LightGBM回归 12.9 因子分解机回归 12.10 AdaBoost回归 12.11 KNN回归 12.12 高斯过程回归 12.13 多层感知机回归 【第十…

基于YOLOv8+LSTM的商超扶梯场景下行人安全行为姿态检测识别

基于YOLOv8LSTM的商超扶梯场景下行人安全行为姿态检测识别 手扶电梯 行为识别 可检测有人正常行走&#xff0c;有人 跌倒&#xff0c;有人逆行三种行为 跌倒检测 电梯跌倒 扶梯跌倒 人体行为检测 YOLOv8LSTM。 基于YOLOv8LSTM的商超扶梯场景下行人安全行为姿态检测识别&#xf…

小程序构建npm失败

小程序构建npm失败 项目工程结构说明解决方法引入依赖导致的其他问题 今天在初始化后的小程序中引入TDesign组件库&#xff0c;构建npm时报错。 项目工程结构说明 初始化后的项目中&#xff0c;包含miniprogram文件夹和一些项目配置文件&#xff0c;在project.config.json文件中…

最新简洁大方的自动发卡网站源码/鲸发卡v11.61系统源码/修复版

源码简介&#xff1a; 最新简洁大方的自动发卡网站源码&#xff0c;它就是鲸发卡v11.61系统源码&#xff0c;它是修复版。 说到鲸发卡系统&#xff0c;鲸发卡系统在发卡圈很多人都知道的&#xff0c;它是市面最好发卡系统之一&#xff0c;操作起来简单得很&#xff0c;界面也…

手机在网状态查询接口如何用PHP进行调用?

一、什么是手机在网状态查询接口&#xff1f; 手机在网状态查询接口&#xff0c;即输入手机号码查询手机号在网状态&#xff0c;返回有正常使用、停机、在网但不可用、不在网&#xff08;销号/未启用/异常&#xff09;、预销户等多种状态。 二、手机在网状态查询适用哪些场景…

无人机视角下的车辆数据集

车辆数据集 无人机视角下的车辆数据集。数据集为无人机俯拍的真实场景下的车辆机动车数据集。数据集已经标注好&#xff0c;yolo格式&#xff0c;txt标签。数据集已经划分好训练集&#xff08;20970张图片&#xff09;验证集&#xff08;5242张图片&#xff09;测试集&#xff…

Redis的主从模式、哨兵模式、集群模式

最近学习了一下这三种架构模式&#xff0c;这里记录一下&#xff0c;仅供参考 目录 一、主从架构 1、搭建方式 2、同步原理 3、优化策略&#xff1a; 4、总结&#xff1a; 二、哨兵架构 1、搭建哨兵集群 2、RedisTemplate如何使用哨兵模式 三、分片集群架构 1&#…

Python用TOPSIS熵权法重构粮食系统及期刊指标权重多属性决策MCDM研究|附数据代码...

原文链接&#xff1a;https://tecdat.cn/?p37724 在当今世界&#xff0c;粮食系统的稳定性至关重要。尽管现有的全球粮食系统在生产和分配方面表现出较高的效率&#xff0c;但仍存在大量人口遭受饥饿以及诸多粮食安全隐患。与此同时&#xff0c;在学术领域&#xff0c;准确评估…

Golang | Leetcode Golang题解之第430题扁平化多级双向链表

题目&#xff1a; 题解&#xff1a; func dfs(node *Node) (last *Node) {cur : nodefor cur ! nil {next : cur.Next// 如果有子节点&#xff0c;那么首先处理子节点if cur.Child ! nil {childLast : dfs(cur.Child)next cur.Next// 将 node 与 child 相连cur.Next cur.Chi…

汉王手写签批控件如何在谷歌、火狐、Edge等浏览器使用

背景 近日&#xff0c;有网友咨询汉王手写签批控件是否可以通过allWebPlugin中间件技术加载到谷歌、火狐、Edge等浏览器&#xff1f;为此&#xff0c;笔者详细了解了一下汉王手写签批控件&#xff0c;它是一个标准的ActiveX控件&#xff0c;曾经主要在IE浏览器使用&#xff0c;…

【计算机基础】用bat命令将Unity导出PC包转成单个exe可执行文件

Unity打包成exe可执行文件 上边连接是很久以前用过的方法&#xff0c;发现操作有些不一样了&#xff0c;并且如果按上述操作比较麻烦&#xff0c;所以写了个bat命令。 图1、导出的pc程序 如图1是导出的pc程序&#xff0c;点击exe文件可运行该程序。 添加pack_project.bat文件 …

大数据Flink(一百二十二):阿里云Flink MySQL连接器介绍

文章目录 阿里云Flink MySQL连接器介绍 一、特色功能 二、​​​​​​​语法结构 三、​​​​​​​​​​​​​​WITH参数 阿里云Flink MySQL连接器介绍 阿里云提供了MySQL连接器&#xff0c;其作为源表时&#xff0c;扮演的就是flink cdc的角色。 一、特色功能 MySQ…

操作系统 | 学习笔记 | | 王道 | 5.3 磁盘和固态硬盘

5.3 磁盘和固态硬盘 5.3.1 磁盘 磁盘结构 磁盘&#xff1a;磁盘的表面由一些磁性物质组成&#xff0c;可以用这些磁性物质来记录二进制数据 磁道&#xff1a;磁盘的盘面被划分成一个个磁道。这样的一个“圈”就是一个磁道 扇区&#xff1a;一个磁道又被划分成一个个扇区&am…

大数据毕业设计选题推荐-网络电视剧收视率分析系统-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、PHP、.NET、Node.js、GO、微信小程序、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇…

通信工程学习:什么是NFVO网络功能虚拟化编排器

NFVO&#xff1a;网络功能虚拟化编排器 NFVO&#xff08;Network Functions Virtualization Orchestrator&#xff09;&#xff0c;即网络功能虚拟化编排器&#xff0c;是网络功能虚拟化&#xff08;NFV&#xff09;架构中的核心组件之一。NFV是一种将传统电信网络中的网络节点…