保护共享数据的初始化过程

在多线程编程中,互斥量是最通用的保护共享数据的机制。但是在某些情况下,一些资源仅需要在第一次初始化的时候需要保护,其时候就可以不需要互斥变量的保护了。比如编码中最常见的单例模式,核心代码如下:

//(3)获得本类实例的唯一全局访问点
static CSinglton* GetInstance()
{//若实例不存在,则创建实例对象if (NULL == pInstance){pInstance = new CSinglton();}
//实例已经存在,直接该实例对象return pInstance;
}

以上代码在多线程环境中是不安全的,有可能导致创建对象两次,导致内存泄漏;如果每次访问之前都是加锁,将大大影响访问性能,即使能解决问题。

为了满足多线程环境中高效和安全的特性,人们提出了双重锁定方式单例模式,代码如下:

static CSinglton* GetInstance()
{//仅在实例未被创建时加锁,其他时候直接返回if (NULL == pInstance){std::lock_guard<std::mutex> guard(g_mutex)if (NULL == pInstance){//若实例不存在,则创建实例对象pInstance = new CSinglton();//步骤一}}//实例已经存在,直接该实例对象return pInstance;
}
//执行代码
GetInstance()->dosomething();//步骤二

但是在实践中可以发现,会因为指令编排方式不同,导致一些异常情况发生。
因为线程A中pInstance分配了指针,CSinglton的构造函数还没有执行,但此时线程B开始调用GetInstance()接口,因为pInstance已经非空,直接执行dosomething(),就导致异常情况发生了。

同样的,创建静态局部变量的代码,在C++11之前也存在抢着创建变量的问题:

class my_class;
my_class& get_my_class_instance()
{static my_class instance;  // 多个线程同时创建实例。return instance;
}

std::call_once保护共享数据
为了解决双重锁定以及创建多个实例的问题,C++11标准库提供了std::once_flag和std::call_once来处理只初始化一次的情况。使用std::call_once比显式使用互斥量消耗的资源更少,并且还是无锁式编程,减少了死锁问题的发生。

call_once和once_flag的用法:

std::once_flag flag;void simple_do_once()
{std::call_once(flag, [](){ std::cout << "Simple example: called once\n"; });
}int _tmain(int argc, _TCHAR* argv[])
{std::thread st1(simple_do_once);std::thread st2(simple_do_once);std::thread st3(simple_do_once);std::thread st4(simple_do_once);st1.join();st2.join();st3.join();st4.join();std::cout << "main thread end\n";
}

运行结果:

Simple example: called once//只有一个打印,目标函数仅被执行一次
main thread end

如何用std::call_once创建安全性的单例模式参见这篇文章call_once 使用方法

发现问题
在正常情况下,若在执行期间call_once调用的函数抛出异常,则once_flag状态不会翻转,其他线程还可以继续执行call_once;但是在vs2013测试发现,异常无法安装预期进行,不知道是何种原因,这里做个记录。

测试代码:

std::once_flag flag;void may_throw_function(bool do_throw)
{if (do_throw) {std::cout << "throw: call_once will retry\n"; // 这会出现多于一次throw std::exception();}std::cout << "Didn't throw, call_once will not attempt again\n"; // 保证一次
}void do_once(bool do_throw)
{try {std::call_once(flag, may_throw_function, do_throw);}catch (...) {}
}int main(int argc, _TCHAR* argv[])
{std::thread t1(do_once, true);std::thread t2(do_once, true);std::thread t3(do_once, false);std::thread t4(do_once, true);t1.join();t2.join();t3.join();t4.join();std::cout << "main thread end\n";
}

运行结果:

vs2013编译的程序无法继续执行,线程卡住,如下

td::call_once的替代方案
在前面的文章中提到,我们也可以使用静态局部变量的方式创建唯一实例,只是在多线程环境中,存在这么一种情况:每个线程都认为他们是第一个初始化这个变量,导致这个变量被创建两次。

但在C++11标准中,这些问题都被解决了:初始化及定义完全在一个线程中发生,并且没有其他线程可在初始化完成前对其进行处理,条件竞争终止于初始化阶段。当然,这个要求编译要支持C++11才可以。

std::call_once的替代方案
class my_class;
my_class& get_my_class_instance()
{static my_class instance;  // 线程安全的初始化过程return instance;
}

本文转自:C++11保护共享数据的其他方法_Keep Moving~-CSDN博客

C++11保护共享数据的其他方法相关推荐

  1. C++11使用互斥量保护共享数据

    C++中使用互斥量 在C++11中,可以通过实例化std::mutex创建互斥量,可以通过调用成员函数lock()进行上锁,调用unlock()进行解锁. 例如: int g_num = 0; std ...

  2. python跨文件全局变量_Python 进程之间共享数据(全局变量)的方法

    进程之间共享数据(数值型): import multiprocessing def func(num): num.value=10.78 #子进程改变数值的值,主进程跟着改变 if __name__= ...

  3. python进程共享全局变量 时延_Python 进程之间共享数据(全局变量)的方法

    进程之间共享数据(数值型): import multiprocessing def func(num): num.value=10.78 #子进程改变数值的值,主进程跟着改变 if __name__= ...

  4. 正确使用锁保护共享数据,协调异步线程

    JMQ为提升性能,使用近乎无锁的设计: MQ中的锁是个必须使用的技术 使用锁会降低系统性能 如何正确使用锁? 异步和并发设计可大幅提升性能,但程序更复杂:多线程执行时,充斥不确定性.对一些需并发读写的 ...

  5. 使用互斥元保护共享数据-lock_guard

    用互斥元保护共享数据 线程相对于进程的优势在于能够共享数据,线程相对于进程的劣势也在于数据能够共享.如何多个线程安全的访问数据,你可以使用互斥锁保护数据,也可以优化数据结构使用无锁编程,或者使用事务保 ...

  6. 用区块链保护共享数据?存储初创公司Gospel开始试水

    初创公司Gospel Technology声称正在利用区块链(Blockchain)来保护和验证可共享的数据.Gospel称,区块链技术的方法可以做到这一点,而且要比任何替代方法更简单. Gospel ...

  7. kali 去windows共享数据--smbclient使用方法_原水_新浪博客

    kali已经安装好smbclient,不能使桌面化的smb访问windows共享文件 1  smbclient --user=username //10.1.0.1 2  然后通过cd,ls等找到需要 ...

  8. Disruptor 线程间共享数据无需竞争

    队列的作用是缓冲 缓冲到 队列的空间里.. 线程间共享数据无需竞争 原文 地址  作者  Trisha   译者:李同杰 LMAX Disruptor 是一个开源的并发框架,并获得2011 Duke' ...

  9. 《C++并发编程实战》读书笔记——chapter 3_线程间共享数据

    更多的阅读笔记,及示例代码见 Github https://github.com/anlongstory/C-_Concurrency_in_Action_reading_notes 本章主要内容: ...

最新文章

  1. 2021陇南高考成绩查询,2021年陇南中考成绩公布查询时间 陇南中考成绩查询方式入口...
  2. 说说你对 SVG 理解?
  3. 笔记本电脑怎么清理灰尘_笔记本电脑玩游戏发热怎么办?笔记本玩游戏发烫解决方法...
  4. mysql命令查看过程内容_mysql查看存储过程命令
  5. React 快速上手 - 目录索引
  6. 沈熙-JavaScript引擎原理及优化
  7. 十五、static关键字
  8. CountDownLatch的两种常用场景
  9. Javascript基础知识笔记三
  10. c语言定积分的基本思想_积分的计算方法、技巧、思路总结~
  11. 数据库范式那些事[转]
  12. (void) (_x == _y)
  13. SLA技术3D打印机的原理
  14. 【响应式Web前端设计】i标签和em标签的区别
  15. ggplot2设置坐标轴范围_6.2 坐标轴:对连续变量设置坐标轴显示范围
  16. 第六十九章 Caché 函数大全 $WCHAR 函数
  17. 电商项目—会员体系、等级、折扣营销解析
  18. IP分片(一)【羊羊洒洒的Blog】
  19. html设置手机为数字,用数字4636设置手机网络类型的方法步骤
  20. 2020年度开发者工具Top 100名单!

热门文章

  1. php系统函数代码,PHP自定义函数+系统函数库(代码示例)
  2. 自己动手写CPU(8)加载存储指令的实现
  3. 复合型法matlab,复合形法程序出错求大神改错急急急
  4. xp系统web服务器搭建教程,Windows_XP配置WEB服务器教程(图)
  5. postgresql点云las_点云模型_点云模型_模型_时空数据库_PolarDB PostgreSQL 云原生数据库 - 阿里云...
  6. 电脑销售渠道_双十一遇上英雄联盟S10,苏宁游戏装备销售增长258%
  7. 完全平方数 HYSBZ - 2440 (莫比乌斯函数容斥)
  8. 1.4激活函数-带隐层的神经网络tf实战
  9. Arduino MEGA 2560找不到驱动怎么办
  10. UIImagePickerController和UIAlertController结合使用