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() 唤醒所有正在等待条件的线程,使它们重新进入竞争状态。

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ansbile-变量

文章目录 一、Ansible的常量&#xff08;内置的变量&#xff09;有哪些&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1…

vulnhub(11):derpnstink(hydra爆破用户名和密码、验证的文件上传)

端口 nmap主机发现 nmap -sn 192.168.159.120/24 ​ Nmap scan report for 192.168.159.120 Host is up (0.00020s latency). ​ 120是新出现的机器&#xff0c;他就是靶机 nmap端口扫描 nmap -Pn 192.168.159.120 -p- --min-rate 10000 -oA nmap/scan 扫描开放端口保存到 nma…

【论文笔记】Are Large Kernels Better Teacheres than Transformers for ConvNets

Abstract 本文提出蒸馏中小核ConvNet做学生时&#xff0c;与Transformer相比&#xff0c;大核ConvNet因其高效的卷积操作和紧凑的权重共享&#xff0c;使得其做教师效果更好&#xff0c;更适合资源受限的应用。 用蒸馏从Transformers蒸到小核ConvNet的效果并不好&#xff0c;原…

图的应用(关键路径)

基于你设计的带权有向无环图&#xff0c;写出所有合法的关键路径&#xff0c;并算出关键路径总长度 文字描述&#xff1a;关键路径总长度的现实意义是什么&#xff1f; 1.关键路径 总长度454316 2.现实意义 从源点到汇点的所有路径中&#xff0c;具有最大路径长度的路径称…

好的头戴式降噪耳机一定很贵吗?四款热门头戴耳机盘点及推荐!

在快节奏的现代生活中&#xff0c;噪音无处不在&#xff0c;它常常干扰着我们的工作、学习与休闲时光。而一款高性价比的降噪蓝牙耳机&#xff0c;就如同一个贴心的伙伴&#xff0c;能为我们营造出一片宁静的听觉空间。如今&#xff0c;耳机市场蓬勃发展&#xff0c;想要好的头…

Broadcast:Android中实现组件及进程间通信

目录 一&#xff0c;Broadcast和BroadcastReceiver 1&#xff0c;简介 2&#xff0c;广播使用 二&#xff0c;静态注册和动态注册 三&#xff0c;无序广播和有序广播 1&#xff0c;有序广播的使用 2&#xff0c;有序广播的截断 3&#xff0c;有序广播的信息传递 四&am…

如何在GitHub上克隆仓库:HTTPS、SSH和GitHub CLI的区别

GitHub是开发者的天堂&#xff0c;提供了丰富的工具和功能来管理代码和项目。在克隆GitHub仓库时&#xff0c;你可能会遇到三种常见的方法&#xff1a;HTTPS、SSH和GitHub CLI。每种方法都有其独特的优势和适用场景。本文将深入探讨这三种克隆方式的区别&#xff0c;帮助你选择…

C++--类的实例化

一、实例化的概念 用类类型在屋里内存中创建对象的过程&#xff0c;称为类实例化出对象 类是对对象进行一种抽象描述&#xff0c;是一个模型一样的东西&#xff0c;限定了类有哪些成员变量&#xff0c;这些成员变量只是声明&#xff0c;没有分配空间&#xff0c;用类实例化出…

java项目之编程训练系统源码(springboot)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的编程训练系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 编程训练系统的主要使用者管…

【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上)

文章目录 前言一、ArkTS基本介绍1、 ArkTS组成2、组件参数和属性2.1、区分参数和属性的含义2.2、父子组件嵌套 二、装饰器语法1.State2.Prop3.Link4.Watch5.Provide和Consume6.Observed和ObjectLink代码示例&#xff1a;示例1&#xff1a;&#xff08;不使用Observed和ObjectLi…

未来通信抢先看!遨游通讯2024年中国国际信息通信展亮点剧透

2024年中国国际信息通信展览会将于9月25日-27日在北京国家会议中心举行&#xff0c;本届展会以“推动数实深度融合&#xff0c;共筑新质生产力”为主题。在通信技术日新月异的今天&#xff0c;卫星通信、人工智能、低碳节能等技术理念正引领着通信行业迈向新的高度。遨游通讯作…

【漏洞复现】Casbin casdoor static 任意文件读取漏洞

漏洞描述 Casdoor 是 Casbin 开源社区推出的基于 OAuth 2.0 / OIDC 的中心化的单点登录(SSO)身份验证平台。 Casdoor static 存在任意文件读取漏洞,攻击者通过发送特殊的请求包可以获取服务器中的敏感文件。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵…

Linux C# DAY3

作业 1、 #!/bin/bash mkdir -p ~/dir/dir1 mkdir ~/dir/dir2 cp ./* ~/dir/dir1 cp ./*.sh ~/dir/dir2 cd ~/dir/ tar -cvJf dir2.tar.xz ./dir2 mv dir2.tar.xz ~/dir/dir1/ cd ~/dir/dir1/ tar -xvf dir2.tar.xz 2、 #!/bin/bash head -5 /etc/group | tail -1 sudo mkdi…

CORS漏洞及其防御措施:保护Web应用免受攻击

1. 背景- 什么是CORS&#xff1f; 在当今互联网时代&#xff0c;Web 应用程序的架构日益复杂。一个后端服务可能对应一个前端&#xff0c;也可能与多个前端进行交互。跨站资源共享&#xff08;CORS&#xff09;机制在这种复杂的架构中起着关键作用&#xff0c;但如果配置不当&…

Redis Key的过期策略

Redis 的过期策略主要是指管理和删除那些设定了过期时间的键&#xff0c;以确保内存的有效使用和数据的及时清理。 具体来说&#xff0c;Redis 有三种主要的过期策略&#xff1a;定期删除&#xff08;Scheduled Deletion&#xff09;、惰性删除&#xff08;Lazy Deletion&#…

鸿蒙Harmony-Next 徒手撸一个日历控件

本文将介绍如何使用鸿蒙Harmony-Next框架实现一个自定义的日历控件。我们将创建一个名为CalendarView的组件&#xff08;注意,这里不能叫 Calendar因为系统的日历叫这个&#xff09;,它具有以下功能: 显示当前月份的日历支持选择日期显示农历日期可以切换上一月和下一月 组件…

情感类智能体——你的微信女神

智能体名称&#xff1a;你的微信女神 链接&#xff1a;文心智能体平台AgentBuilder | 想象即现实 (baidu.com)https://agents.baidu.com/agent/preview/RulbsUjIGj4wsinydlBH7AR3NQKFungt 简介 “你的微信女神”是一个直率的智能体&#xff0c;她用犀利而真实的言辞帮助用户…

C++第十一节课 new和delete

一、new和delete操作自定义类型 new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数&#xff08;new会自动调用构造函数&#xff1b;delete会调用析构函数&#xff09; class A { public:A(int a 0): _a(a){cout <&l…

JAVAWeb--前端工程化

一、前端工程化开篇 1.1 什么是前端工程化 前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块化、组件化、规范化、自动化的问题,其主要目的为了提高效率和降低成本。 1.2 前端工程化实现技术栈 前端工程化实现的技术栈有很多,我们采用ES6nodejsnpmViteVUE3route…

【C++ Primer Plus习题】16.10

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: #include <iostream> #include <string> #include <…