std::call_once 是 C++11 提供的线程安全机制,确保某函数在多线程下仅执行一次,需配合 std::once_flag 使用;flag 必为静态生命周期,异常不标记完成,性能优于手动 mutex,语义清晰且异常安全。

std::call_once 是 C++11 引入的线程安全工具,用于确保某个函数(或可调用对象)在多线程环境下**只被执行一次**,即使多个线程同时调用它。它常和 std::once_flag 配合使用,是实现线程安全单次初始化(如单例、资源首次加载、全局状态设置等)的核心机制。
核心组成:std::once_flag + std::call_once
必须成对使用:
-
std::once_flag 是一个不可复制、不可移动的标记对象,用来记录“是否已执行过”;每个需要单次初始化的逻辑应有自己独立的 red">static 或全局
std::once_flag实例。 -
std::call_once(flag, func, ...) 接收一个
once_flag&和一个可调用对象(支持函数指针、lambda、bind、成员函数等),以及可选参数。它会阻塞其他竞争线程,直到该可调用对象成功执行完毕——且仅有一个线程真正执行,其余线程等待后直接返回。
典型用法示例:延迟初始化全局资源
比如加载配置、创建线例、初始化日志系统:
std::shared_ptrg_config; std::once_flag g_config_init_flag; void init_config() { g_config = std::make_shared ("config.json"); } // 多个线程可能同时调用这个函数 std::shared_ptr get_config() { std::call_once(g_config_init_flag, init_config); return g_config; }
无论多少线程并发调用 get_config(),init_config() 只运行一次,g_config 初始化安全无竞态。
立即学习“C++免费学习笔记(深入)”;
关键注意事项
-
std::once_flag必须是 static 或具有静态生命周期(如全局、类 static 成员),否则每次调用都新建 flag,失去“一次”的语义。 - 若传入的函数抛出异常,
std::call_once会捕获并重新传播给当前线程;但该次调用被视为“失败”,flag 不置位——下次调用仍会尝试执行(即:异常 ≠ 已完成)。 - 内部基于原子操作和轻量级锁实现,开销极小,远低于手写 double-checked locking 或 mutex 全局保护。
- 不适用于需要“首次调用即初始化 + 后续调用需等待完成”的场景?其实它就是干这个的——所有线程都会等到那个唯一成功执行的线程完成才继续。
和 std::mutex 对比:为什么更推荐 call_once?
相比手动用 mutex 加锁做单次检查:
- 代码更简洁,语义更明确:“我要确保这事只干一次”;
- 无需担心忘记 unlock、死锁、异常安全(lock_guard 自动释放)等问题;
- 标准库可针对平台优化(如 Linux 下可能用 futex),性能通常更好;
- 天然支持函数对象和参数转发,灵活度高。
基本上就这些。用好 std::call_once,能帮你避开大量多线程初始化的坑。










