前言
某些场景下,需要代码只被执行一次,比如单例类的初始化,考虑到多线程安全,需要进行加锁控制。C++11中提供的
call_once
可以很好的满足这种需求,使用又非常简单。
一、std::call_once
1、函数声明
函数定义于头文件
<mutex>
,call_once
保证可调用对象_Fx
只被执行一次,即使在多线程场景,函数原型:
template<class _Fn, class... _Args> inline
void (call_once)(once_flag& _Flag, _Fn&& _Fx, _Args&&... _Ax)
_Flag
:标志对象,用于指示_Fx
是否已调用过_Fx
:可调用对象_Ax
:传递的参数
使用
call_once
注意的事项:
- 如果在调用
call_once
的时刻,_Flag
指示_Fx
已经调用过,那么call_once
会立即返回 - 如果在调用
_Fx
时抛出了异常,那么异常将传播给call_once
的调用方,并且_Flag
不会被翻转 - 如果调用正常返回,那么
_Flag
被翻转,并保证以同一_Flag
对call_once
的其他调用立即返回 - 如果有多个线程同时在
_Flag
未翻转时调用call_once
,那么这些调用将被组成单独全序,并被依次执行
2、std::once_flag
用来表示可调用对象是否成功执行过,
std::once_flag
是不允许修改的,其拷贝构造函数和operator=
函数都声明为delete
,声明如下:
struct once_flag
{ // opaque data structure for call_once()
constexpr once_flag() _NOEXCEPT
: _Opaque(0)
{ // default construct
}
once_flag(const once_flag&) = delete;
once_flag& operator=(const once_flag&) = delete;
void *_Opaque;
};
注意:还有一个要注意的地方是
once_flag
的生命周期,它必须要比使用它的线程的生命周期要长。所以通常定义成全局变量比较好。
3、应用示例
使用
call_once
实现一个单例模式,如下:
static std::once_flag oc; // 用于call_once的局部静态变量
Singleton* Singleton::m_instance;
Singleton* Singleton::getInstance() {
std::call_once(oc, [&] () {
m_instance = new Singleton();
});
return m_instance;
}