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

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:
    /// mutex
    T& 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:
    /// mutex
    pthread_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,可以用来实现低开销的线程安全操作。
原子操作通常用于简单的赋值、递增、递减等操作,而且是无锁的,所以开销比互斥量和自旋锁都要小。
总结:
使用互斥量时,长时间锁定资源会使其他线程挂起,适合复杂操作。
使用自旋锁时,线程会忙等待直到获取锁,适合短时间锁定资源。
使用原子操作时,可以保证单一操作的线程安全,无需锁定,开销最小,但仅限于简单操作(因为只有两种状态)。

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

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

相关文章

ElementUI中的el-table表格实现动态添加一行、删除一行、清空所有行

ElementUI中的el-table表格实现动态添加一行、删除一行、清空所有行 1、需求分析2、代码实现HTMLdatamethods 1、需求分析 ElementUI中的el-table中实现动态添加一行、删除一行、清空所有行 2、代码实现 HTML <div class"middle-wrapper"><el-buttontype…

Golang hash/crc32 库实战指南:从基础到优化

Golang hash/crc32 库实战指南&#xff1a;从基础到优化 引言理解CRC32hash/crc32库概览实战技巧数据校验性能优化多线程应用 错误处理与调试错误处理调试 实际案例分析结论 总结重点回顾 引言 在现代软件开发中&#xff0c;数据的完整性和安全性至关重要。无论是数据库存储、…

Python抓取抖音直播间数据:技术探索与实践

目录 一、引言 二、技术准备 三、分析抖音直播间网页结构 四、编写爬虫代码 五、处理反爬虫机制 六、数据清洗与存储 七、总结 一、引言 随着互联网的快速发展&#xff0c;直播行业已成为当下的热门领域。抖音作为其中的佼佼者&#xff0c;吸引了大量的用户和主播。对于…

使用vue构建一个简单实用的春节红包插件!

摘要&#xff1a;本文将介绍如何使用Vue.js构建一个简单实用的春节红包插件。该插件通过模拟红包的打开和关闭过程&#xff0c;以及金额的随机分配&#xff0c;为春节红包活动提供了一个有趣且互动的体验。 一、引言 在春节这个充满欢乐和祝福的时刻&#xff0c;红包成为了传递…

Avalonia11.0.2+.Net6.0支持多语言,国际化使用DynamicResource绑定数据

Avalonia11.0.2+.Net6.0支持多语言,国际化使用DynamicResource绑定数据 介绍调整的内容效果展示介绍 本章内容是对上一章博客的补充,当时我们用的是自定义扩展的方式实现了多语言数据的绑定,本章我们用标准的 Text="{DynamicResource 名称}" 来替换 Text="{i…

iphoneX系统的参数

1. 2. 3. 4. 5.相关的网址信息 Apple iPhone X 規格、价格和评论 | Kalvo Apple iPhone X 規格、价格和评论 | Kalvo

Android ViewPager2 setOffscreenPageLimit预加载Fragment,Kotlin

Android ViewPager2 setOffscreenPageLimit预加载Fragment&#xff0c;Kotlin import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androi…

社交革命:Facebook如何塑造数字社交的未来

引言 在当今数字化时代&#xff0c;社交媒体已成为人们生活的核心&#xff0c;而Facebook作为其中的领军者&#xff0c;一直在塑造着数字社交的未来。本文将深入探讨Facebook在数字社交领域的地位、影响力以及对未来社交的塑造作用&#xff0c;为读者揭示这场社交革命如何由Fa…

华为开源自研AI框架昇思MindSpore应用案例:梯度累加

目录 一、环境准备1.进入ModelArts官网2.使用CodeLab体验Notebook实例 二、案例实现 梯度累加的训练算法&#xff0c;目的是为了解决由于内存不足&#xff0c;导致Batch size过大神经网络无法训练&#xff0c;或者网络模型过大无法加载的OOM&#xff08;Out Of Memory&#xff…

华为实验-基于用户和应用的安全策略

CLI举例&#xff1a;基于用户和应用的安全策略 通过配置安全策略&#xff0c;实现基于用户、时间段以及应用的访问控制。 组网需求 如图1所示&#xff0c;某企业在网络边界处部署了FW作为安全网关。 企业根据员工级别和职能不同划分了三种用户&#xff1a;高层管理者、市场员…

OSG编程指南<二十一>:OSG视图与相机视点更新设置及OSG宽屏变形

1、概述 什么是视图?在《OpenGL 编程指南》中有下面的比喻,从笔者开始学习图形学就影响深刻,相信对读者学习场景管理也会非常有帮助。 产生目标场景视图的变换过程类似于用相机进行拍照,主要有如下的步骤: (1)把照相机固定在三脚架上,让它对准场景(视图变换)。 (2)…

The Annotated Transformer 阅读学习

查资料的间隙发现一篇介绍Transformer的文章&#xff0c;觉得写得很好&#xff0c;但是时间有限一时半会没办法深入去读这里就做了简单的阅读记录&#xff0c;英语水平有限这里只好借助于机器翻译的帮助&#xff0c;将阅读的内容记录下来&#xff0c;等后续有时间再来回顾。 原…

前端-html-02

1.列表 标签名功能和语义属性单标签还是双标签ul无序列表包裹元素双标签 ol 有序列表包裹元素双标签li列表项双标签dl定义列表包裹元素双标签dt定义列表项标题双标签dd定义列表项描述双标签 li必须由Ul或者ol包裹 <!DOCTYPE html> <html><head><…

Linux(CentOS)/Windows-C++ 云备份项目(服务器网络通信模块,业务处理模块设计,断点续传设计)

此模块将网络通信模块和业务处理模块进行了合并 网络通信通过httplib库搭建完成业务处理&#xff1a; 文件上传请求&#xff1a;备份客户端上传的文件&#xff0c;响应上传成功客户端列表请求&#xff1a;客户端请求备份文件的请求页面&#xff0c;服务器响应文件下载请求&…

vector类(一)

文章目录 vector介绍和使用1.vector的介绍2.vector的使用2.1 vector的定义2.2 vector iterator的使用2.3 vector空间增长问题2.4 vector增删查改2.5 vector迭代器失效问题 3.vector 在OJ中的使用 vector介绍和使用 1.vector的介绍 vector是表示 可变大小数组的 序列容器。 就…

【jmeter+ant+jenkins】之搭建 接口自动化测试平台

平台搭建 (1). 录制jmeter脚本 (2). 将jmeter的安装目录下的G:\jmeter\apache-jmeter-5.1.1\extras中&#xff0c;将 ”ant-jmeter-1.1.1.jar”文件放到 ant的lib目录下 (3). 配置jmeter的xml配置文件&#xff0c;并放在ant目录的bin目录下&#xff0c;使用ant编译验证jmeter的…

【书生·浦语大模型实战营第二期】学习笔记1

1. Introduction 开源llm举例&#xff1a;LLaMA 、Qwen 、Mistral 和Deepseek 大型语言模型的发展包括预训练、监督微调&#xff08;SFT&#xff09;和基于人类反馈的强化学习&#xff08;RLHF&#xff09;等主要阶段 InternLM2的显著特点 采用分组查询注意力&#xff08;GQA…

2014年认证杯SPSSPRO杯数学建模C题(第一阶段)土地储备方案的风险评估全过程文档及程序

2014年认证杯SPSSPRO杯数学建模 C题 土地储备方案的风险评估 原题再现&#xff1a; 土地储备&#xff0c;是指市、县人民政府国土资源管理部门为实现调控土地市场、促进土地资源合理利用目标&#xff0c;依法取得土地&#xff0c;进行前期开发、储存以备供应土地的行为。土地…

保姆级指导0基础如何快速搭建“对话机器人”类ChatGPT

参考了CDSN上的文章&#xff0c;但发现不work&#xff0c; 不是这里有问题&#xff0c;就是那里有问题&#xff0c;查阅了大量的资料&#xff0c;做了无数次试验&#xff0c;终于整理出来了一个完整的教程&#xff0c;保可用&#xff0c;保真~~~~~如果各位遇到什么问题&#xf…

【Leetcode每日一题】 递归 - 计算布尔二叉树的值(难度⭐⭐)(44)

1. 题目解析 题目链接&#xff1a;2331. 计算布尔二叉树的值 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 算法思路概述&#xff1a; 问题解释&#xff1a;我们面对的是一个节点可能含有逻辑运算符&#xff08;AN…