1. std::once_flag基础概念解析
std::once_flag是C++11标准库中引入的一个同步原语,专门用于配合std::call_once函数实现线程安全的单次初始化。这个看似简单的类在多线程编程中扮演着关键角色——它确保某个操作无论被多少线程调用、调用多少次,都只会执行一次。
从实现角度看,std::once_flag本质上是一个状态标志,内部维护了一个原子状态变量。这个状态可以是以下三种之一:
- not_yet_called:初始状态,表示关联的函数尚未被调用
- executing:表示有线程正在执行目标函数
- called:表示目标函数已经完成执行
关键特性:std::once_flag既不可拷贝也不可移动,这是为了保证其内部状态的唯一性和线程安全性。任何拷贝或移动操作都会导致编译错误。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心工作机制与实现原理
2.1 与std::call_once的协同工作
std::once_flag本身并不直接提供同步功能,它必须与std::call_once配合使用。典型的使用模式如下:
cpp复制std::once_flag flag;
void initialize() {
std::call_once(flag, [](){
// 初始化代码
});
}
当多个线程同时调用std::call_once时,会发生以下序列:
- 第一个到达的线程检查flag状态,发现是not_yet_called,于是将其改为executing并执行目标函数
- 其他线程在第一个线程完成前到达时,会阻塞等待
- 当第一个线程完成执行后,状态变为called,其他线程被唤醒并直接返回
- 任何后续的调用都会直接返回,不会再次执行目标函数
2.2 内存序与原子操作
std::once_flag的内部实现依赖于原子操作和内存屏障。现代编译器通常使用类似如下的内存序:
- 状态读取使用memory_order_acquire
- 状态写入使用memory_order_release
- 状态转换使用memory_order_acq_rel
这种内存序选择确保了:
- 目标函数的所有副作用对后续线程可见(happens-before关系)
- 不会出现指令
