C++ 并发编程 | std::call_once

文章目录

  • 一、std::call_once
    • 1、函数声明
    • 2、std::once_flag
    • 3、应用示例

前言

某些场景下,需要代码只被执行一次,比如单例类的初始化,考虑到多线程安全,需要进行加锁控制。C++11中提供的call_once可以很好的满足这种需求,使用又非常简单。

一、std::call_once

1、函数声明

函数定义于头文件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 被翻转,并保证以同一 _Flagcall_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;
}

你可能感兴趣的:(c++,java,javascript)