首页 > 编程语言 >使用单例模式进行多线程编程

使用单例模式进行多线程编程

时间:2023-10-19 15:34:20浏览次数:33  
标签:NULL MyCAS 编程 instance 线程 单例 static 多线程

title: 
aliases: 
tags:
  - 工程技术
  - cpp/并发编程
category:
  - 方法
stars: 
url: 
creation-time: 2023-10-19 14:32
modification-time: 2023-10-19 15:27:06

[[单例模式]]简而言之就是程序中的某个类只能实例化一个对象。因为对象只有一个,在不同线程中实例化的时候,实际上都是获得那个唯一对象的引用或者指针,那么如果通过这个唯一的对象去共享数据,就可以实现多线程中的数据共享。

单例模式类的设计

首先看一下 [[Cpp]] 中如何实现单例模式的类的设计

class MyCAS {
 private:
  MyCAS() {}                                 // 构造函数私有,禁止通过普通的方式实例化
 
 private:
  static MyCAS* m_instance;

 public:
  static MyCAS* Getinstance() {
    if (m_instance == NULL) {
      m_instance = new MyCAS();
      static CGarhuishou cl;                 // 生命周期一直到程序退出
    }
    return m_instance;
  }
  class CGarhuishou {                        // 用于管理单例对象资源的类
   public:
    ~CGarhuishou() {
      if (MyCAS::m_instance) {
        delete MyCAS::m_instance;
        MyCAS::m_instance = NULL;
      }
    }
  };
  void func() { cout << "测试" << endl; }      // 其他成员函数
};

// 在源码文件中对静态成员变量进行定义和初始化
MyCAS* MyCAS::m_instance=NULL;                // 类的静态成员变量定义和初始化

注意到该类的构造函数是用 private 修饰的,这样就不能创建基于该类的对象了。例如下面的代码都将无法编译通过,这正是单例类要达到的效果。

MyCAS a1;                 // 非法
MyCAS *pa = new MyCAS();  // 非法

只能通过以下方式实例化对象:

MyCAS *p_a = MyCAS::Getinstance();

这里就创建了一个 MyCAS 对象的指针。
另外,由 CGarhuishou 类来管理 m_instance 指针所指向资源的回收。

单例模式应用并发编程

首先建议单例模式的类在所有子线程启动前实例化,这样可以避免一些多线程访问对象资源冲突的问题。
如果我们不这样做,可能会发生什么?想象一下,子线程 1 和子线程 2 中同时进行实例化,当子线程 1 在执行了

if (m_instance == NULL)

时,程序切换到了子线程 2,此时子线程 2 也执行到了

if (m_instance == NULL)

这样,两个线程都会认为 m_instance == NULLtrue,那么还是会执行两次

m_instance = new MyCAS();

为了解决这个问题,可以使用互斥锁+双重检查的方式。

static MyCAS* Getinstance() {
  if (m_instance == NULL) {
    std::unique_lock<std::mutex> mymutex(resource_mutex);
    if (m_instance == NULL) {
      m_instance = new MyCAS();
      static CGarhuishou cl;  // 生命周期一直到程序退出
    }
  }
  return m_instance;
}

因为在多线程中 m_instance == NULL 成立不一定代表唯一执行 m_instance = new MyCAS();,很有可能切换线程了。但是使用互斥锁就能在几个线程同时 new 的时候只有一个继续往下实例化,而当实例化完后释放互斥锁,其他线程再一次检查 m_instance == NULL 时就已经是 FALSE 了。

另一种写法

使用 std::call_once 保证函数只被调用一次。假设有个函数,名字为 acall_once 的功能就是能够保证函数 a 只被调用一次。读者都知道,例如有两个线程都调用函数 a,那么这个函数 a 肯定是会被调用两次。但是,有了 call_once,就能保证,即便是在多线程下,这个函数 a 也只会被调用一次。
代码实现:

std::once_flag g_flag;         //这是一个系统定义的标记

实际上 call_once 就是控制这个 flag 来实现,函数只能被单次调用。
另外在 MyCAS 里面再新增一个函数

private:
 static void CreateInstance(){
  m_instance = new MyCAS();
  static CGarhuishou cl;
 }

并修改 Getinstance() 函数

static MyCAS* Getinstance() {
  if (m_instance == NULL) {
   std::call_once(g_flag, CreateInstance);
  }
  return m_instance;
}

这样,即使是两个线程都判断 m_instance == NULL 成立并进入内部作用域后,CreateInstance 仍然只会执行一次,确保只会实例化一个对象。

Reference

[[C++新经典]]

标签:NULL,MyCAS,编程,instance,线程,单例,static,多线程
From: https://www.cnblogs.com/pomolnc/p/17774826.html

相关文章

  • Linux shell编程学习笔记8:使用字符串
    一、前言字符串是大多数编程语言中最常用最有用的数据类型,这在Linuxshell编程中也不例外。本文讨论了LinuxShell编程中的字符串的三种定义方式的差别,以及字符串拼接、取字符串长度、提取字符串、查找子字符串等常用字符串操作,,以及反引号在echo和expr命令联合使用时的作用。二......
  • Linux shell编程学习笔记4:修改命令行提示符格式(内容和颜色)
    一、命令行提示符格式内容因shell类型而异Linux终端命令行提示符内容格式则因shell的类型而异,例如CoreLinux默认的shell是sh,其命令行提示符为黑底白字,内容为:tc@box:/$其中,tc为当前用户名,box为主机名,/为当前目录路径,$表示当前用户类型是普通用户 。 二、环境变量PS1存储了命令行提......
  • python脚本中应用多线程和多进程理解
    脚本内容因为要读取mongo某个全表数据(亿级别),有个字段有索引且是一堆多的关系从其他表读取所有这个字段(十万级别),再读取大表因为数据量大所以写个测试,从中拿出几条去大表查询(每次读到十万级别数据)多线程和多进程的影响不使用多线/进程file=open('test2.csv','w')content......
  • 实验2 C语言分支与循环基础应用编程
    实验任务1 #include<stdio.h>#include<stdlib.h>#include<time.h>#defineN5#defineN1374#defineN2465intmain(){intnumber;inti;srand(time(0));for(i=0;i<N;++i){number=rand()%(N2-N......
  • 单例
    什么是单例?单例模式是指:保证一个类仅有一个实例,并提供一个访问它的全局访问点#线程1执行:cursor.excute('select*fromuser')#线程2执行cursor.excute('select*frombooks')#线程1执行cursor.fetchAll()#拿出查询到的数据#django,每个线程,一个连接对象-......
  • 开发设计模式之单例模式
    一、单例模式这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。注意:......
  • 多线程
    #include<stdio.h>#include<pthread.h>#include<Windows.h>staticintg_num=0;staticintg_c=0;pthread_mutex_tgMutex_num=PTHREAD_MUTEX_INITIALIZER;pthread_mutex_tgMutex_c=PTHREAD_MUTEX_INITIALIZER;void*test(void*para)......
  • 单例
    单例模式单例是23种设计模式的一种,最简单的一种https://www.cnblogs.com/liuqingzheng/p/10038958.html什么是单例模式?单例模式指的是:保证一个类仅有一个实例,并提供一个访问它的全局访问点线程1执行:cursor.excute('select*fromuser')线程2执行:cursor.excute('select*......
  • 单例模式
    使用模块其实,**Python的模块就是天然的单例模式**,因为模块在第一次导入时,会生成`.pyc`文件,当第二次导入时,就会直接加载`.pyc`文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这......
  • 【玩转Python系列【小白必看】Python多线程爬虫:下载表情包网站的图片
    前言本文主要介绍了使用Python编写的多线程爬虫程序,用于下载表情包网站上的图片。通过解析网页内容和使用XPath定位,可以获取到图片的URL,并将其保存到本地。1.导入模块和库importrequestsfromlxmlimportetreefromthreadingimportThreadfromqueueimportQueueim......