『 Linux 』线程安全的单例模式,自旋锁与读写锁

news2025/1/22 13:11:59

文章目录

    • 单例模式
      • 懒汉模式与饿汉模式
    • 自旋锁
    • 读写锁


单例模式

请添加图片描述

单例模式是一种创建型设计模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点来访问该实例;

这在需要严格控制如何及合适访问某个唯一资源型下有一定作用;

单利模式的主要特点为如下:

  • 私有构造函数

    单例模式通常要将构造私有化,以保证无法直接通过该类实例化出对应的对象;

    只能通过该类提供对应的接口来实例化整个对象,确保只有一个实例;

  • 私有静态实例

    私有静态实例保证实例只能在类内部进行访问,外部代码无法直接操作这个实例;

    可防止外部代码以外或恶意修改改实例;

    由于静态实例是私有的,可保证无论创建多少类的对象,静态成员始终只有一份;

    在单例模式中以该特性确保整个程序中只有一个这样的实例;

  • 公有静态函数

    公有静态函数提供全局访问点来获取唯一的实例;

    确保只允许外部代码通过该静态函数来访问类中唯一的实例;

    一般情况下如果该实例还未存在时该方法将会创建一个新的实例,如果已存在则直接返回现有实例;

单例模式的优点:

  • 保证一个类只有一个实例从而减少内存开销
  • 避免对资源的多重占用
  • 提供了对唯一实例的全局访问点

懒汉模式与饿汉模式

请添加图片描述

懒汉模式与饿汉模式属于单利模式加载方式的其中一种;

  • 懒汉加载模式

    懒汉模式指的是在进程启动时不立马加载实例,而是等到需要用到实例的时候再对实例进行加载;

    该加载方式由于单例在进程启动时未被加载从而能够有效提升进程整体的加载速度;

    但如果多个执行流同时在需要的时候加载实例时可能涉及到临界资源竞争问题,即懒汉模式不是线程安全的,故在以懒汉模式进行设计时应该要确保线程安全,如各个线程应通过互斥锁保持其互斥关系以避免临界资源竞争的问题;

    class SingletonPattern {
     public:
      // 获取单例实例的静态方法
      static SingletonPattern* getSP() {
        // 如果实例不存在,则创建一个新实例
        if (!sp_) sp_ = new SingletonPattern();
        // 返回单例实例
        return sp_;
      }
    
     private:
      // 私有构造函数,防止外部直接创建实例
      SingletonPattern() {
        // 无限循环,持续输出信息并暂停1秒
        while (1) {
          cout << "SingletonPattern is running..." << endl;
          sleep(1);
        }
      }
    
     private:
      // 静态成员变量,用于存储单例实例
      static SingletonPattern* sp_;
    };
    
    // 静态成员变量初始化为nullptr
    SingletonPattern* SingletonPattern::sp_ = nullptr;
    
    int main() {
      // 主程序开始前暂停3秒
      sleep(3);
      // 获取单例实例
      SingletonPattern* sp = SingletonPattern::getSP();
      return 0;
    }
    
    

    在这个例子中主函数开始时程序暂停3s;

    然后调用getSP()以触发实例的创建,一但实例被创建将开始进入构造函数;

  • 饿汉加载模式

    饿汉模式指的是在进程启动时直接将实例进行加载;

    饿汉模式设计是天然线程安全的,不需要额外的同步机制;

    但由于实例是在进程启动时加载,对应的进程启动的时间也会变慢;

    同时如果实例从未被使用则会造成资源浪费;

    // 单例模式类
    class SingletonPattern {
     public:
      // 获取单例实例的静态方法
      static SingletonPattern* getSP() {
        // 如果实例不存在,则创建一个新实例
        if (!sp_) sp_ = new SingletonPattern();
        // 返回单例实例
        return sp_;
      }
    
     private:
      // 私有构造函数,防止外部直接创建实例
      SingletonPattern() {
        // 构造函数的实现(此处为空)
      }
    
      // 静态成员变量,用于存储单例实例
      static SingletonPattern* sp_;
    };
    
    // 静态成员变量的初始化
    // 这里直接调用getSP(),确保在程序启动时就创建实例
    SingletonPattern* SingletonPattern::sp_ = SingletonPattern::getSP();
    
    // 主函数
    int main() {
      // 主函数为空,但单例实例已在程序启动时创建
      return 0;
    }
    

    在这个例子中,由于实例的加载是静态的,故主函数未被加载时单例已经被加载;

[基于懒汉模式加载的单例模式线程池参考代码 - gitee]


自旋锁

请添加图片描述

自旋锁是一种用于多线程同步的低级锁机制;

当一个线程尝试读取一个被其他线程持有的自旋锁时,它将会一直循环检查锁是否可用而不是挂起等待;

  • 互斥锁与自旋锁的区别

    当一个线程试图使用一个被其他线程占用的互斥锁时该线程将进入阻塞等待,直到互斥锁被解除占用时将会唤醒这个线程;

    当一个线程试图使用一个被其他线程占用的自旋锁时该线程不会进入阻塞等待,而是不停尝试申请锁资源;

POSIX线程库提供了一个自旋锁为pthread_spinlock_t类型;

当需要使用该库的自旋锁时必须声明一个该类型的锁变量;

  • 自旋锁的初始化与销毁

    自旋锁的初始化与销毁通常使用pthread_spin_init()pthread_spin_destroy();

    PROLOG
           This  manual  page is part of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (con‐
           sult the corresponding Linux manual page for details of Linux behavior), or the  interface  may  not  be  implemented  on
           Linux.
    
    NAME
           pthread_spin_destroy, pthread_spin_init - destroy or initialize a spin lock object (ADVANCED REALTIME THREADS)
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_spin_destroy(pthread_spinlock_t *lock);
           int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
    
    RETURN VALUE
           Upon successful completion, these functions shall return zero; otherwise, an error number shall be returned  to  indicate
           the error.
    
    

    这两个函数调用成功时返回0,调用失败时返回一个错误码(非0);

    两个函数所传递的第一个参数都为一个pthread_spinlock_t *类型的自旋锁指针;

    pthread_spin_init()函数的第二个参数pshared设置该自旋锁是否为共享属性:

    • PTHREAD_PROCESS_PRIVATE

      表示自旋锁只能在初始化它的进程内线程之间的共享;

    • PTHREAD_PROCESS_SHARED

      表示自旋锁可以在多个进程中的线程之间共享;

    pthread_spin_init()函数用于初始化自旋锁,pthread_spin_destroy()用于释放该自旋锁;

  • 自旋锁的锁定

    通常使用pthread_spin_lock()锁定自旋锁;

    PROLOG
           This  manual  page is part of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (con‐
           sult the corresponding Linux manual page for details of Linux behavior), or the  interface  may  not  be  implemented  on
           Linux.
    
    NAME
           pthread_spin_lock, pthread_spin_trylock - lock a spin lock object (ADVANCED REALTIME THREADS)
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_spin_lock(pthread_spinlock_t *lock);
           int pthread_spin_trylock(pthread_spinlock_t *lock);
    
    RETURN VALUE
           Upon  successful  completion, these functions shall return zero; otherwise, an error number shall be returned to indicate
           the error.
    

    其中pthread_spin_lock()函数为尝试获取一个自旋锁,如果该自旋锁被其他线程持有,则一直自旋直至成功获取锁;

    pthread_spin_trylock()函数则是尝试获取一个自旋锁,如果该锁被其他线程持有则立即返回不进行自旋;

    两个函数的参数都为传递一个pthread_spinlock_t *的自旋锁对象指针;

    当函数调用成功时返回0,失败时则返回一个错误码;

  • 自旋锁的解锁

    通常使用pthread_spin_unlock()对自旋锁进行解锁;

    PROLOG
           This  manual  page is part of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (con‐
           sult the corresponding Linux manual page for details of Linux behavior), or the  interface  may  not  be  implemented  on
           Linux.
    
    NAME
           pthread_spin_unlock - unlock a spin lock object (ADVANCED REALTIME THREADS)
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_spin_unlock(pthread_spinlock_t *lock);
    
    DESCRIPTION
           The  pthread_spin_unlock()  function  shall  release  the  spin  lock  referenced  by  lock  which  was  locked  via  the
           pthread_spin_lock() or pthread_spin_trylock() functions. The results are undefined if the lock is not held by the calling
           thread. If there are threads spinning on the lock when pthread_spin_unlock() is called, the lock becomes available and an
           unspecified spinning thread shall acquire the lock.
    
           The results are undefined if this function is called with an uninitialized thread spin lock.
    
    RETURN VALUE
           Upon successful completion, the pthread_spin_unlock() function shall return zero; otherwise, an  error  number  shall  be
           returned to indicate the error.
    
    

    该函数用于解锁一个自旋锁,当函数调用成功时返回0,调用失败时则返回一个错误码;

    其中参数传递表示传递一个pthread_spinlock_t *的自旋锁对象指针;


读写锁

请添加图片描述

读写锁也被称为共享锁/独占锁,允许多个线程同时读取共享资源,但在写入时只允许一个线程访问;

读写锁分为两种状态:

  • 读锁(共享锁)

    表示多个线程可以同时持有读锁,当线程持有读锁时其他线程无法再获取写锁;

  • 写锁(独占锁)

    表示只有一个线程可以持有写锁,同时此时不能有任何读锁;

读者写者问题本质上也可以理解为一个生产者消费者模型的问题,具有两个角色,三种关系和一个交易场所;

  • 两个角色

    读者与写者;

  • 三种关系

    • 写者与写者的互斥关系

    • 写者与读者的互斥与同步关系

    • 读者与读者的共享关系

      读者与读者的共享关系主要是因为其只对数据进行读取,不对数据进行覆盖或修改等操作;

  • 一个交易场所

    指的是以一个特定结构的内存空间(临界区);

POSIX线程库中同样提供了读写锁;

在该库中的读写锁类型为pthread_rwlock_t,当需要使用读写锁时必须使用该类型声明一个读写锁变量;

  • 读写锁的初始化与销毁

    通常使用pthread_rwlock_init()pthread_rwlock_destroy()对读写锁进行初始化与销毁;

    PROLOG
           This  manual  page is part of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (con‐
           sult the corresponding Linux manual page for details of Linux behavior), or the  interface  may  not  be  implemented  on
           Linux.
    
    NAME
           pthread_rwlock_destroy, pthread_rwlock_init - destroy and initialize a read-write lock object
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
           int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
                  const pthread_rwlockattr_t *restrict attr);
    
    RETURN VALUE
           If  successful,  the  pthread_rwlock_destroy() and pthread_rwlock_init() functions shall return zero; otherwise, an error
           number shall be returned to indicate the error.
    
           The [EBUSY] and [EINVAL] error checks, if implemented, act as if they were performed immediately at the beginning of pro‐
           cessing  for  the  function  and  caused an error return prior to modifying the state of the read-write lock specified by
           rwlock.
    
    

    其中两个函数的第一个参数都为pthread_rwlock_t *类型的读写锁指针变量,以初始化或销毁该读写锁;

    两个函数调用成功时都会返回0,调用失败时则会返回一个非0错误码;

    pthread_rwlock_init()的第二个参数attr表示需要传递一个读写锁属性对象的指针,若是传递nullptr表示使用默认属性;

    同时也可使用PTHREAD_RWLOCK_INITIALIZER宏来静态初始化读写锁(静态初始化的读写锁可不需要调用pthread_rwlock_init()pthread_rwlock_destroy()对读写锁进行初始化与释放);

  • 写者加锁

    通常使用pthread_rwlock_wrlock()函数用于读者加锁;

    PROLOG
           This  manual  page is part of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (con‐
           sult the corresponding Linux manual page for details of Linux behavior), or the  interface  may  not  be  implemented  on
           Linux.
    
    NAME
           pthread_rwlock_trywrlock, pthread_rwlock_wrlock - lock a read-write lock object for writing
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
           int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    
    RETURN VALUE
           The pthread_rwlock_trywrlock() function shall return zero if the lock for writing on the read-write  lock  object  refer‐
           enced by rwlock is acquired. Otherwise, an error number shall be returned to indicate the error.
    
           If  successful,  the  pthread_rwlock_wrlock() function shall return zero; otherwise, an error number shall be returned to
           indicate the error.
    

    其中pthread_rwlock_wrlock()表示线程尝试获取一个写锁,如果该读写锁被其他线程(读锁或是写锁)占用则阻塞;

    pthread_rwlock_trywrlock()表示线程尝试获取一个写锁,如果该读写锁被其他线程占用则返回不进行阻塞;

    两个函数所传递的参数pthread_rwlock_t *rwlock表示传递一个读写锁对象的指针;

    两个函数调用成功时都会返回0,调用失败时则会返回一个非0错误码;

  • 读者加锁

    通常使用pthread_rwlock_rdlock()函数用于读者加锁;

    PROLOG
           This  manual  page is part of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (con‐
           sult the corresponding Linux manual page for details of Linux behavior), or the  interface  may  not  be  implemented  on
           Linux.
    
    NAME
           pthread_rwlock_rdlock, pthread_rwlock_tryrdlock - lock a read-write lock object for reading
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
           int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    
    RETURN VALUE
           If  successful,  the  pthread_rwlock_rdlock() function shall return zero; otherwise, an error number shall be returned to
           indicate the error.
    
           The pthread_rwlock_tryrdlock() function shall return zero if the lock for reading on the read-write  lock  object  refer‐
           enced by rwlock is acquired. Otherwise, an error number shall be returned to indicate the error.
    

    其中pthread_rwlock_rdlock()表示线程尝试获取一个读锁,如果该读写锁被其他线程以写锁占用则阻塞,否则与其他持有读锁的线程共享该写锁;

    pthread_rwlock_tryrdlock()表示线程尝试获取一个读锁,如果该读写锁被其他线程以写锁占用则返回不进行阻塞,否则与其他持有读锁的线程共享该锁;

    两个函数所传递的参数pthread_rwlock_t *rwlock表示传递一个读写锁对象的指针;

    两个函数调用成功时都会返回0,调用失败时则会返回一个非0错误码;

  • 读写锁的解锁

    通常使用pthread_rwlock_unlock()函数来解锁读写锁;

    PROLOG
           This  manual  page is part of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (con‐
           sult the corresponding Linux manual page for details of Linux behavior), or the  interface  may  not  be  implemented  on
           Linux.
    
    NAME
           pthread_rwlock_unlock - unlock a read-write lock object
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    
    RETURN VALUE
           If  successful,  the  pthread_rwlock_unlock() function shall return zero; otherwise, an error number shall be returned to
           indicate the error.
    

    其中传递的参数表示传递一个需要解锁的读写锁对象指针;

    该函数调用成功时返回0,失败时返回一个错误码;

通常情况下在使用该锁时读者的数量必定大于写者,且当读者在读的时候写者获取读写锁时将进行阻塞,这个现象可能导致写者长时间得不到锁资源所产生饥饿问题,这个现象被称为读者优先;

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

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

相关文章

使用Dockerfile构建一个包含NVIDIA的PyTorch和Detectron2的镜像

查看Dockerfile 以下是详细的解释&#xff1a; # 使用更具体的标记来固定基础镜像版本&#xff0c;确保环境一致性 FROM nvcr.io/nvidia/pytorch:23.01-py3# 设置工作目录和环境变量 WORKDIR /root ENV DETECTRON2_PATH /root/detectron2# 复制并安装 Detectron2 COPY detect…

YOLOv10模型训练、验证、推理

前言 yolov10关于模型的各种参数其实都写到了一起&#xff08;包括训练、验证和推理的参数&#xff09;&#xff0c;在./ultralytics/cfg/default.yaml中&#xff0c;通过使用这些指令我们可以实现各种所需的操作。 代码地址&#xff1a;https://github.com/THU-MIG/yolov10 …

【Material-UI 组件】Autocomplete 中的 Grouped 功能详解

文章目录 一、组件概述1.1 Grouped 功能介绍1.2 适用场景 二、基础用法2.1 实现 Grouped 功能代码拆解 三、高级配置3.1 自定义组渲染3.2 常见配置 四、最佳实践4.1 数据排序4.2 组标题优化4.3 性能优化4.4 可访问性 五、总结 Grouped 功能使得 Autocomplete 组件能够按特定维度…

Linux系统驱动(三)ioctl函数

文章目录 一、ioctl函数&#xff08;一&#xff09;函数格式&#xff08;二&#xff09;ioctl命令码的组成1. 命令码的组成2. 自己封装命令码2. 内核提供了封装命令码的宏 &#xff08;三&#xff09;使用示例1. 驱动2. 应用 一、ioctl函数 Linux内核开发者想要将数据的读写和…

c++ 21 指针

*像一把钥匙 通过钥匙去找内存空间 间接修改内存空间的值 不停的给指针赋值 等于不停的更改指针的指向 指针也是一种数据类型 指针做函数参数怎么看都不可以 指针也是一个数据类型 是指它指向空间的数据类习惯 作业 野指针 向null空间地址copy数据 不断改变指针指向 …

JVM的组成 -- 字节码文件

类加载器(ClassLoader)&#xff1a;将字节码文件加载到内存中运行时数据区(JVM管理的内存)&#xff1a;负责管理JVM使用的内存&#xff0c;比如创建对象和销毁对象执行引擎&#xff1a;即时编译器、解释器、垃圾回收器。负责本地接口的调用本地接口&#xff1a;native方法&…

高等数学 第八讲 积分学计算_不定积分_定积分_反常积分的计算

高等数学 第八讲 积分学计算 文章目录 高等数学 第八讲 积分学计算1.不定积分的计算1.1 基本积分公式1.2 不定积分的计算方法1.2.1 凑微分法1.2.2 换元法1.2.3 分布积分法1.2.4 有理函数的积分计算(待更新)1.2.5 不定积分的一些计算结论总结 2.定积分的计算2.1 牛顿莱布尼茨公式…

大数据Flink(一百零八):阿里云与开源的功能优势对比

文章目录 阿里云与开源的功能优势对比 阿里云与开源的功能优势对比 下面通过表格介绍阿里云实时计算Flink全托管产品的功能点和价值&#xff0c;以及和开源Flink的对比优势。 类型 功能 描述 价值 性能与成本 资源利用率提升 可以根据业务负载进行弹性扩缩容。 更好的…

手摸手教你前端和后端是如何实现导出 Excel 的?

前言 大家好呀&#xff0c;我是雪荷。在上篇文章&#xff08;EasyExcel 初使用—— Java 实现多种写入 Excel 功能-CSDN博客&#xff09;中给大家介绍了 Java 是如何写入 Excel 的&#xff0c;那么这篇算是对上篇文章的拓展&#xff0c;主要介绍前端和后端分别是如何导出数据至…

一篇了解: MyBatis-Plus 操作数据库的使用

目录 一、MyBatis-Plus介绍 二、基础使用 2.1 准备工作 2.2 编码 2.3 CRUD单元测试 三、MyBatis-Plus复杂操作 3.1 打印日志 3.2 常见注解 3.2.1 TableName 3.2.2 TableField 3.2.3 TableId 3.3 条件构造器 3.3.1 QueryWrapper 3.3.2 UpdateWrapper 3.3.3 Lamb…

网络空间安全专业怎么样,可通过哪些途径自学?

网络空间安全主要研究网络空间的组成、形态、安全、管理等&#xff0c;进行网络空间相关的软硬件开发、系统设计与分析、网络空间安全规划管理等。例如&#xff0c;网络犯罪的预防&#xff0c;国家网络安全的维护&#xff0c;杀毒软件等安全产品的研发&#xff0c;网络世界的监…

计算机常识与NOIP历史-CSP初赛知识点整理

真题练习 [2021-CSP-J-第2题] 以下奖项与计算机领域最相关的是&#xff08; &#xff09;。 A.奥斯卡奖 B.图灵奖 C.诺贝尔奖 D.普利策奖 [2017-NOIP-第7题] 中国计算机学会于( )年创办全国青少年计算机程序设计竞赛。 A. 1983 B. 1984 C. 1985 D. 1986 [2018-NOIP-第5题…

Professional Scrum Master (PSM) 官方认证培训班:掌握Scrum,提升项目管理能力

在快速变化的商业环境中&#xff0c;高效的项目管理和团队协作是企业成功的关键。作为一种广泛认可的敏捷框架&#xff0c;Scrum已成为推动项目成功和提高团队效率的重要工具。为了帮助专业人士掌握Scrum方法和实践&#xff0c;Scrum.org推出了Professional Scrum Master (PSM)…

C++ 初探:不要‘下次一定’,立即开始你的C++之旅

C初识 文章目录 C hello worldnamespace&#xff0c;命名空间命名空间的使用域作用限定符展开命名空间指定展开命名空间成员 C的域 C的输入和输出缺省参数函数重载引用&#xff08;reference&#xff09;引用概念引用的特性引用的使用const引用 inlinenullptr C hello world #…

学习C语言遇到的问题

前言 这是博主在班训班培训期间学习C基础过程中遇到的一些问题&#xff0c;我把遇到的问题以及这些问题的解答链接统一放在这篇文章了。这只是博主遇到的一些问题&#xff0c;可能不会适用于全部人&#xff0c;希望大家把这篇文章当做查漏补缺的内容吧。 问题一:把字符串赋给…

三十种未授权访问漏洞复现 合集( 三)

未授权访问漏洞介绍 未授权访问可以理解为需要安全配置或权限认证的地址、授权页面存在缺陷&#xff0c;导致其他用户可以直接访问&#xff0c;从而引发重要权限可被操作、数据库、网站目录等敏感信息泄露。---->目录遍历 目前主要存在未授权访问漏洞的有:NFS服务&a…

CnosDB 元数据集群 – 分布式时序数据库的大脑

CnosDB 是一个分布式时序数据库系统&#xff0c;其中元数据集群是核心组件之一&#xff0c;负责管理整个集群的元数据信息。 1. 概述 CnosDB 是一个分布式时序数据库系统&#xff0c;其中元数据集群是核心组件之一&#xff0c;负责管理整个集群的元数据信息。元数据包括数据库…

用Ollama 和 Open WebUI本地部署Llama 3.1 8B

说明&#xff1a; 本人运行环境windows11 N卡6G显存。部署Llama3.1 8B 简介 Ollama是一个开源的大型语言模型服务工具&#xff0c;它允许用户在自己的硬件环境中轻松部署和使用大规模预训练模型。Ollama 的主要功能是在Docker容器内部署和管理大型语言模型&#xff08;LLM&…

[CR]厚云填补_GridFormer

GridFormer: Residual Dense Transformer with Grid Structure for Image Restoration in Adverse Weather Conditions Abstract 恶劣天气条件下的图像恢复是计算机视觉中的一个难点。在本文中&#xff0c;我们提出了一种新的基于变压器的框架GridFormer&#xff0c;它可以作为…

【Android】ContentProvider基本概念

ContentProvider Android权限机制详解 <manifest xmlns:android"http://schemas.android.com/apk/res/android"package"com.example.broadcasttest"> <uses-permission android:name"android.permission.RECEIVE_BOOT_COMPLETED" />…