协程库-锁类-实现线程互斥同步

mutex.h:信号量,互斥锁,读写锁,范围锁模板,自旋锁,原子锁

**锁不能进行拷贝操作:**锁是用于管理多线程并发访问共享资源的同步原语。这些锁包括互斥锁(mutex)、读写锁(rwlock)等。它们通常不支持拷贝构造和拷贝赋值,这是为了防止在一个线程持有锁的情况下,另一个线程通过拷贝得到相同的锁,从而可能导致死锁或数据不一致的问题。

《Effective C++》 条款 06

想要禁止⼀个类对象的拷⻉操作,就要禁⽌拷⻉构造函数和拷⻉赋值运算符。
解决⽅案2:
定义⼀个基类专⻔阻⽌拷⻉动作,继承该基类的派生类起实例化对象也就无法进行拷⻉操作。

/*** @file noncopyable.h* @brief 不可拷贝对象封装*/
#ifndef __Fzk_NONCOPYABLE_H__
#define __Fzk_NONCOPYABLE_H__
namespace FzkCoroutine {
/*** @brief 对象无法拷贝,赋值*/
class Noncopyable {
public:/*** @brief 默认构造函数*/Noncopyable() = default;/*** @brief 默认析构函数*/~Noncopyable() = default;/*** @brief 拷贝构造函数(禁用)*/Noncopyable(const Noncopyable&) = delete;/*** @brief 赋值函数(禁用)*/Noncopyable& operator=(const Noncopyable&) = delete;
};
}
#endif

基于pthread进一步封装了信号量,互斥锁,读写锁,范围锁模板,自旋锁,原子锁。

局部锁模板:

采用RAII编程风格,RAII的核心思想是利用对象的生命周期来管理资源,确保资源在对象的构造函数中被获取,并在析构函数中被释放。
ScopedLockImpl 及其派生类通过在构造时获取资源(加锁)并在析构时释放资源(解锁)

/*** @brief 局部锁的模板实现*/
template<class T>
struct ScopedLockImpl {
public:/*** @brief 构造函数* @param[in] mutex Mutex*/ScopedLockImpl(T& mutex):m_mutex(mutex) {m_mutex.lock();m_locked = true;}/*** @brief 析构函数,自动释放锁*/~ScopedLockImpl() {unlock();}/*** @brief 加锁*/void lock() {if(!m_locked) {m_mutex.lock();m_locked = true;}}/*** @brief 解锁*/void unlock() {if(m_locked) {m_mutex.unlock();m_locked = false;}}
private:/// mutexT& m_mutex;/// 是否已上锁bool m_locked;
};

类似于C++的lock_guard:

lock_guard通过与互斥锁(mutex)结合使用来实现线程同步。当创建一个lock_guard对象时,获取提供给它的互斥锁的所有权,并自动调用互斥锁的lock()方法来加锁。如果互斥锁已经被其他线程锁定,当前线程将会阻塞,直到互斥锁被解锁。
当lock_guard对象离开作用域时,它的析构函数会被自动调用。在析构函数中,lock_guard会调用互斥锁的unlock()方法来解锁互斥锁。这样可以确保即使在异常情况下,互斥锁也能被正确解锁,避免死锁的发生。
lock_guard特点:
1、创建即加锁,作⽤域结束⾃动析构并解锁,⽆需⼿⼯解锁
2、不能中途解锁,必须等作⽤域结束才解锁
3、不能复制

#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx; // 定义一个互斥锁
int counter = 0; // 共享资源void increment() {for (int i = 0; i < 100000; ++i) {std::lock_guard<std::mutex> lock(mtx); // 加锁++counter; // 访问共享资源}
}
int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Counter: " << counter << std::endl;return 0;
}

互斥量Mutex:

/*** @brief 互斥量*/
class Mutex : Noncopyable { //继承不可拷贝数据类
public: /// 局部锁typedef ScopedLockImpl<Mutex> Lock;/*** @brief 构造函数*/Mutex() {pthread_mutex_init(&m_mutex, nullptr);}/*** @brief 析构函数*/~Mutex() {pthread_mutex_destroy(&m_mutex);}/*** @brief 加锁*/void lock() {pthread_mutex_lock(&m_mutex);}/*** @brief 解锁*/void unlock() {pthread_mutex_unlock(&m_mutex);}
private:/// mutexpthread_mutex_t m_mutex;
};

读写互斥量RWMutex:

/*** @brief 读写互斥量*/
class RWMutex : Noncopyable{
public:/// 局部读锁typedef ReadScopedLockImpl<RWMutex> ReadLock;/// 局部写锁typedef WriteScopedLockImpl<RWMutex> WriteLock;/*** @brief 构造函数*/RWMutex() {pthread_rwlock_init(&m_lock, nullptr);}/*** @brief 析构函数*/~RWMutex() {pthread_rwlock_destroy(&m_lock);}/*** @brief 上读锁*/void rdlock() {pthread_rwlock_rdlock(&m_lock);}/*** @brief 上写锁*/void wrlock() {pthread_rwlock_wrlock(&m_lock);}/*** @brief 解锁*/void unlock() {pthread_rwlock_unlock(&m_lock);}
private:/// 读写锁pthread_rwlock_t m_lock;
};

自旋锁

自旋锁是一种同步机制,它不会导致线程进入睡眠状态,而是通过循环不断尝试获取锁资源,可以避免线程切换的开销。但只适用于锁被持有时间短的场景,自旋等待的时间不会太长,而且可以避免线程切换的开销。如果锁被持有时间较长或竞争激烈导致很多线程在自旋等待,那么自旋锁可能会导致CPU资源的浪费,因为线程在等待时会持续占用CPU周期
此外,自旋锁不适合在中断处理中使用,因为中断处理程序应该尽快完成,避免长时间占用CPU。

/*** @brief 自旋锁*/
class Spinlock : Noncopyable {
public:/// 局部锁typedef ScopedLockImpl<Spinlock> Lock;/*** @brief 构造函数*/Spinlock() {pthread_spin_init(&m_mutex, 0);}/*** @brief 析构函数*/~Spinlock() {pthread_spin_destroy(&m_mutex);}/*** @brief 上锁*/void lock() {pthread_spin_lock(&m_mutex);}/*** @brief 解锁*/void unlock() {pthread_spin_unlock(&m_mutex);}
private:/// 自旋锁pthread_spinlock_t m_mutex;
};

原子锁??感觉就是用原子布尔类型实现的自旋锁

CASLock使用C++11中的std::atomic_flag来实现无锁同步,lock()函数使用std::atomic_flag_test_and_set_explicit()函数尝试获取锁,如果获取失败则一直循环等待。unlock()函数使用std::atomic_flag_clear_explicit()函数释放锁。
定义了一个volatile std::atomic_flag类型的成员变量m_mutex,用于表示锁的状态。由于它是volatile类型的,因此编译器不会对其进行优化,保证了其可见性,每次操作都会从内存读取m_mutex的值。

/*** @brief 原子锁*/
class CASLock : Noncopyable {
public:/// 局部锁typedef ScopedLockImpl<CASLock> Lock;/*** @brief 构造函数*/CASLock() {m_mutex.clear();}/*** @brief 析构函数*/~CASLock() {}/*** @brief 上锁*/void lock() {//获取失败则一直循环等待  感觉和自旋锁没区别了while (std::atomic_flag_test_and_set_explicit(&m_mutex, std::memory_order_acquire));}/*** @brief 解锁*/void unlock() {std::atomic_flag_clear_explicit(&m_mutex, std::memory_order_release);}
private:/// 原子状态volatile std::atomic_flag m_mutex;
};CASLock lock;
int shared_data = 0;void thread_func() {for (int i = 0; i < 100000; ++i) {CASLock::Lock l(lock); // 加锁++shared_data; // 访问共享资源}
}int test2() {std::thread t1(thread_func);std::thread t2(thread_func);t1.join();t2.join();std::cout << "shared_data: " << shared_data << std::endl;return 0;
}

测试效果:
1、加原子锁
在这里插入图片描述
2、未加原子锁
在这里插入图片描述
#include 只能将基本数据类型声明为原子变量

#include<iostream>
#include<thread>
#include<windows.h>
#include<atomic>     //新增
#include<vector>
#include<mutex>        
using namespace std;
void Mythread(atomic<int>& num)    //修改
{for (int i = 0; i < 100000; i++){		num++;	}
}
void test03()
{//int num = 0;std::atomic<int> num = 0;int threadNum = 2;     //线程个数vector<std::thread> m_thread;m_thread = vector<std::thread>(threadNum);for (auto i = 0; i < threadNum; i++){m_thread[i] = std::thread(Mythread, &num);}for (auto i = 0; i < threadNum; i++){m_thread[i].join();}cout <<"结果:"<<num << endl;
}
int main()
{test03();system("pause");return 0;
}

小结一下:

互斥量(Mutex):
互斥量是最基本的同步机制之一。它阻止多个线程同时访问共享资源。
当一个线程锁定互斥量时,如果另一个线程尝试锁定同一个互斥量,它将被阻塞(挂起),直到拥有锁的线程释放该锁。
互斥量适用于锁定时间较长的场景,比如复杂操作或涉及I/O的操作。
在 C++ 中,可以使用 头文件中的 std::mutex 类。
自旋锁(Spinlock):
自旋锁在等待锁释放时,线程会在循环检查锁的状态直到获取锁,它不会使线程挂起,而是占用CPU周期等待。
自旋锁适用于锁定时间非常短的场景,因为它避免了线程挂起和恢复的开销(避免线程切换)。
C++11 标准没有直接提供自旋锁,但可以通过原子操作实现,或者使用平台特定的实现(如 POSIX 的 pthread_spinlock,POSIX“可移植操作系统接口”,能够在多种系统之间使用)。
原子锁(Atomic Lock):
原子操作是指不可分割、不会被线程调度机制打断的操作。在执行完毕之前,不会有其他线程对这个操作进行干扰。
C++11 引入了原子操作库 ,提供了一组原子类型,如 std::atomic,可以用来实现低开销的线程安全操作。
原子操作通常用于简单的赋值、递增、递减等操作,而且是无锁的,所以开销比互斥量和自旋锁都要小。
总结:
使用互斥量时,长时间锁定资源会使其他线程挂起,适合复杂操作。
使用自旋锁时,线程会忙等待直到获取锁,适合短时间锁定资源。
使用原子操作时,可以保证单一操作的线程安全,无需锁定,开销最小,但仅限于简单操作(因为只有两种状态)。

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

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

相关文章

Allegro许可更新流程

解锁企业软件管理新篇章&#xff0c;Allegro许可更新流程助力高效运营 在数字化经济的时代&#xff0c;软件已经成为企业运营的关键要素。然而&#xff0c;随着技术的不断更新换代&#xff0c;软件的许可证也需要随之更新。如何确保软件许可的及时更新&#xff0c;避免因过期或…

AI智能分析网关V4使用GB28181注册到EasyCVR平台的具体步骤

旭帆科技的智能分析网关V4内含近40种智能分析算法&#xff0c;包括人体、车辆、消防、环境卫生、异常检测等等&#xff0c;在消防安全、生产安全、行为检测等场景应用十分广泛。如常见的智慧工地、智慧校园、智慧景区、智慧城管等等&#xff0c;还支持抓拍、记录、告警、语音对…

小黑的Vue前端之路:js中通过构造函数封装,设置对象getter和setter方法

js中构造函数创建对象 JavaScript本身并不是设计成面向对象的,所以没有class之类的关键字用来定义类,但JavaScript本身相当灵活,可以利用function关键字来定义类并创建对象。 例如js创建person对象 通过new关键字&#xff0c;把函数当成了创建对象的构造函数 function Pers…

某康安全开发工程师一面

一、反射型XSS跟DOM型XSS的最大区别 DOM型xss和别的xss最大的区别就是它不经过服务器&#xff0c;仅仅是通过网页本身的JavaScript进行渲染触发的。 二、Oracle数据库了解多吗 平常用的多的是MySQL数据库&#xff0c;像Oracle数据库也有了解&#xff0c;但是用的不多。 三、…

Fastjson反序列化漏洞原理与漏洞复现(基于vulhub靶场)

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

【码银送书第十五期】一本书掌握数字化运维方法,构建数字化运维体系

前言 数字化转型已经成为大势所趋&#xff0c;各行各业正朝着数字化方向转型&#xff0c;利用数字化转型方法论和前沿科学技术实现降本、提质、增效&#xff0c;从而提升竞争力。 数字化转型是一项长期工作&#xff0c;包含的要素非常丰富&#xff0c;如数字化转型顶层设计、…

macOS Sonoma 14.4.1 (23E224) 正式版发布,ISO、IPSW、PKG 下载

macOS Sonoma 14.4.1 (23E224) 正式版发布&#xff0c;ISO、IPSW、PKG 下载 2024 年 3 月 26 日凌晨&#xff0c;macOS Sonoma 14.4.1 更新修复了一个可能导致连接到外部显示器的 USB 集线器无法被识别的问题。它还解决了可能导致 Java 应用程序意外退出的问题&#xff0c;并修…

Linux Sftp和Scp

scp 和 sftp 区别 1 scp 能将远程文件复制到另一个远程机&#xff0c;sftp 不能。sftp为 SSH的其中一部分&#xff0c;是一种传输档案至 Blogger 伺服器的安全方式 2.scp 没有删除/创建远程目录功能&#xff0c;sftp 有。scp 在需要进行验证时会要求你输入密码或口令。 3. FT…

栈与队列的实现逻辑以及底层代码

1.前言 栈和队列不是一种语言独有的结构&#xff0c;而是一个由代码语言设计的一种数据结构。是由人设计出的一种具有特定意义的结构。 2.栈 什么是栈&#xff0c;栈以结构体为节点按要求链接的一种后进先出的一种数据结构&#xff08;last in first out&#xff09;简称LIF…

Docker部署一个SpringBoot项目(超级详细)

注意&#xff1a;下面的教程主要是针对 Centos7 的&#xff0c;如果使用的其他发行版会有细微的差别&#xff0c;请查看官方文档。 Docker部署一个SpringBoot项目&#xff08;超级详细&#xff09; 一、安装Docker1.卸载旧版2.配置Docker的yum库3.安装Docker4.设置开机自启动5.…

通过一篇文章让你了解如何学习C++

如何学习C 前言一、如何学习C二、别人是怎么学C的21天学会C编程能力与编程年龄 三、自己怎么学总结 前言 学习C需要掌握其基础语法、指针和内存管理、STL库使用、面向对象编程等核心概念。可通过阅读权威书籍、在线教程和参考官方文档来系统学习。实践是关键&#xff0c;通过…

网络编程基本概念(一篇文章掌握基本内容的详细概念,IP,端口号,协议,协议分层,封装和分用,客户端和服务端,请求和回应,两台主机的通信)

IP地址 概念 IP地址主要⽤于标识⽹络主机、其他⽹络设备&#xff08;如路由器&#xff09;的⽹络地址。简单说&#xff0c;IP地址⽤于定位主机的⽹络地址。 就像我们发送快递⼀样&#xff0c;需要知道对⽅的收货地址&#xff0c;快递员才能将包裹送到⽬的地。 IP的格式 IP地址…

自动化面试常见算法题!

1、实现一个数字的反转&#xff0c;比如输入12345&#xff0c;输出54321 num 12345 num_str str(num) reversed_num_str num_str[::-1] reversed_num int(reversed_num_str) print(reversed_num) # 输出 54321代码解析&#xff1a;首先将输入的数字转换为字符串&#xff…

HarmonyOS SDK 闭源开放能力 —Scan Kit

1.问题描述 Scan Kit扫描专用底层码流接口需要鉴权&#xff0c;鉴权失败后功能还能用吗&#xff1f; 解决方案 如果已经申请过白名单&#xff0c;因为异常导致的鉴权失败会优先放通&#xff0c;保障业务成功。 2.问题描述 调用Scan Kit扫描专用底层码流接口会不会将二维码…

systemd-journal(三)之systemd.journal-fields

文章目录 写在前面概述用户日志字段&#xff08;User Journal Fields&#xff09;MESSAGEMESSAGE_IDPRIORITYCODE_FILE, CODE_LINE, CODE_FUNCERRNOINVOCATION_ID, USER_INVOCATION_IDSYSLOG_FACILITY, SYSLOG_IDENTIFIER, SYSLOG_PID, SYSLOG_TIMESTAMPDOCUMENTATIONTIDUNIT, …

C++11新特性之并发

std::thread c11引入了std::thread来创建线程&#xff0c;支持对线程join或者detach。直接看代码&#xff1a; #include <iostream> #include <thread>void threadFunction() {std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "I…

渗透测试——分享几个我常用渗透网站

前言 经常有小伙伴问我常用的渗透网站有哪些&#xff0c;花点时间整理出来&#xff0c;废话不多说&#xff0c;直接上网站。 一、雨苁 雨苁 这个网站从我接触网络安全一直在用&#xff0c;里面有几个很有用的工具如&#xff1a;在线地图情报搜集&#xff0c;开源情报信息收集…

【2G 50元/年 4G 618/3年!】支持比价必赔 送抽奖机会 京东云服务器推荐 附阿里云 腾讯云价格对比表

《最新对比表》已更新在文章头部—腾讯云文档&#xff0c;文章具有时效性&#xff0c;请以腾讯文档为准&#xff01; 【腾讯文档实时更新】云服务器1分钟教会你如何选择教程 https://docs.qq.com/document/DV0RCS0lGeHdMTFFV?tab000003 ​ 当前活动&#xff1a;采购季&#…

浮点数 向偶数舍入

二进制中间值是2/21 10.1110101011011保留四位偶数舍入 10.1110 101011011 保留部分 舍弃部分 舍弃部分101011011>中间值1 所以要保留部分进位 最终结果为 10.1111 10.1110 1 保留部分 舍弃部分 舍弃部分1中间值1 此时看保留部分最后一位,如果是奇数就保留…

供应链数字化转型:电子元器件商城的策略与实践

电子元器件商城通过供应链数字化转型&#xff0c;可以提高运营效率、降低成本、加速交付&#xff0c;并增强竞争力。以下是一些策略和实践建议&#xff1a; 数字化采购管理&#xff1a; 利用供应链管理软件或平台&#xff0c;实现采购过程的数字化管理。这包括供应商信息管理、…