once_flag與call_once
頭文件
#include <mutex>
once_flag結構
std::once_flag實例的狀態,指示所關聯的函數尚未被調用。
其構造函數擁有constexpr指定符,以帶有靜態存儲時間段的實例,作爲靜態初始化階段的一部分被構造,避免競爭條件和初始化順序問題。
struct once_flag
{
constexpr once_flag() noexcept : _Opaque(0){}
once_flag(const once_flag&) = delete;
once_flag& operator=(const coce_flag&) = delete;
void *_Opaque;
}
call_once函數模板
在同一std::once_flag對象上,std::call_once調用被序列化。
當且僅當_Fx的調用返回而無異常,std::call_once的調用有效。
若在同一std::once_flag對象上,之前未有過有效的std::call_once調用,參數_Fx(或其副本)如同通過INVOKE(_Fx, _Ax)一樣被調用。
如果在同一std::once_flag對象上,之前有過有效的std::call_once,對std::call_once的後續調用直接返回而不執行_Fx。
template<typename _Fn, typename... _Args>
inline void call_once(once_flag& _Flag, _Fn&& _Fx, _Args&&... _Ax);
用法
有時對於一類對象,只需初始化一次環境,可以採用類似的封裝方式:
#include <mutex>
class Module
{
public:
Module() {}
~Module() {}
void init()
{
static std::once_flag once_init;
std::call_once(once_init, openLibs);
}
private:
static void openLibs() {}
};
錯誤用法
在上述的openLibs函數中,再次間接通過std::call_once調用自己,會導致棧溢出錯誤。這是由於未完成一次有效調用,還未改變std::once_flag的狀態,仍然出現openLibs被多次調用的情況。
參考資料
[1] C++併發編程實戰[M]. [美]Anthony Williams著;周全等譯. 北京:人民郵電出版社,2015