C++中多线程与Singleton的那些事儿
作者:网络转载 发布时间:[ 2015/3/11 15:34:30 ] 推荐标签:C++ 线程 软件开发
但是在C++11却是线程安全的,这是因为新的C++标准规定了当一个线程正在初始化一个变量的时候,其他线程必须得等到该初始化完成以后才能访问它。
在C++11 standard中的§6.7 [stmt.dcl] p4:
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
在stackoverflow中的Is Meyers implementation of Singleton pattern thread safe?这个问题中也有讨论到。
不过有些编译器在C++11之前的版本支持这种模型,例如g++,从g++4.0开始,meyers singleton是线程安全的,不需要C++11。其他的编译器需要具体的去查相关的官方手册了。
Atomic Singleton
在C++11之前的版本下,除了通过锁实现线程安全的Singleton外,还可以利用各个编译器内置的atomic operation来实现。(假设类Atomic是封装的编译器提供的atomic operation)
template<typename T>
class Singleton
{
public:
static T& getInstance()
{
while (true)
{
if (ready_.get())
{
return *value_;
}
else
{
if (initializing_.getAndSet(true))
{
// another thread is initializing, waiting in circulation
}
else
{
value_ = new T();
ready_.set(true);
return *value_;
}
}
}
}
private:
Singleton();
~Singleton();
static Atomic<bool> ready_;
static Atomic<bool> initializing_;
static T* value_;
};
template<typename T>
Atomic<int> Singleton<T>::ready_(false);
template<typename T>
Atomic<int> Singleton<T>::initializing_(false);
template<typename T>
T* Singleton<T>::value_ = NULL;
肯定还有其他的写法,但是思路都差不多,需要区分三种状态:
对象已经构造完成
对象还没有构造完成,但是某一线程正在构造中
对象还没有构造完成,也没有任何线程正在构造中
pthread_once
如果是在unix平台的话,除了使用atomic operation外,在不适用C++11的情况下,还可以通过pthread_once来实现Singleton。
pthread_once的原型为
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
APUE中对于pthread_once是这样说的:
如果每个线程都调用pthread_once,系统能保证初始化例程init_routine只被调用一次,即在系统首次调用pthread_once时。
所以,我们可以这样来实现Singleton了
template<typename T>
class Singleton : Nocopyable
{
public:
static T& getInstance()
{
threads::pthread_once(&once_control_, init);
return *value_;
}
private:
static void init()
{
value_ = new T();
}
Singleton();
~Singleton();
static pthread_once_t once_control_;
static T* value_;
};
template<typename T>
pthread_once_t Singleton<T>::once_control_ = PTHREAD_ONCE_INIT;
template<typename T>
T* Singleton<T>::value_ = NULL;
如果需要正确的释放资源的话,可以在init函数里面使用glibc提供的atexit函数来注册相关的资源释放函数,从而达到了只在进程退出时才释放资源的这一目的。
static object
现在再回头看看本文开头说的面试题的要求,不用锁和C++11,那么可以通过atomic operation来实现,但是有人会说atomic不是夸平台的,各个编译器的实现不一样。那么其实通过static object来实现也是可行的。
template<typename T>
class Singleton
{
public:
static T& getInstance()
{
return *value_;
}
private:
Singleton();
~Singleton();
class Helper
{
public:
Helper()
{
Singleton<T>::value_ = new T();
}
~Helper()
{
delete value_;
value_ = NULL;
}
};
friend class Helper;
static T* value_;
static Helper helper_;
};
template<typename T>
T* Singleton<T>::value_ = NULL;
template<typename T>
typename Singleton<T>::Helper Singleton<T>::helper_;
在进入main之前把Singleton对象构造出来可以避免在进入main函数后的多线程环境中构造的各种情况了。这种写法有一个前提是不能在main函数执行之前调用getInstance,因为C++标准只保证静态变量在main函数之前之前被构造完成。
可能有人会说如果helper的初始化先于value_初始化的话,那么helper_初始化的时候会使用尚没有被初始化的value_,这个时候使用其返回的对象会出现问题,或者在后面value_“真正”初始化的时候会覆盖掉helper_初始化时赋给value_的值。
实际上这种情况不会发生,value_的初始化一定先于helper_,因为C++标准保证了这一行为:
The storage for objects with static storage duration (basic.stc.static) shall be zero-initialized (dcl.init) before any other initialization takes place. Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization. Objects of POD types (basic.types) with static storage duration initialized with constant expressions (expr.const) shall be initialized before any dynamic initialization takes place. Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.
stackoverflow中的一个问题也讨论了相关的行为,When are static C++ class members initialized?
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南