std::call_once 与 std::once_flag 配合可确保函数在多线程环境下仅执行一次,常用于单例初始化、全局配置等场景。通过示例可见,三个线程调用 std::call_once 时,do_init 函数只执行一次,避免了手动加锁带来的竞态、死锁等问题,语义清晰且线程安全。

在c++多线程编程中,有时需要确保某段代码在整个程序生命周期中只执行一次,比如初始化全局资源、单例对象的构造等。std::call_once 就是为此设计的线程安全机制,它能保证即使多个线程同时尝试调用,目标函数也只会被成功执行一次。
std::call_once 的基本用途
std::call_once 通常与 std::once_flag 配合使用。once_flag 是一个标记,用来记录函数是否已经被调用过。call_once 接收这个标记和一个可调用对象(如函数、Lambda),只有当该标记未被设置时,才会执行传入的函数,并自动设置标记防止重复执行。
典型应用场景包括:
- 单例模式中的延迟初始化
- 全局配置或日志系统的首次设置
- 动态加载共享资源(如数据库连接池)
如何使用 std::call_once
使用步骤非常简单:
立即学习“C++免费学习笔记(深入)”;
1. 定义一个全局或静态的 std::once_flag 对象。
2. 在多个线程中调用 std::call_once,传入同一个 once_flag 和初始化函数。
3. 系统自动保证函数仅执行一次。
示例代码:
#include <iostream> #include <thread> #include <mutex> std::once_flag flag; void do_init() { std::cout << "Initialization executed by current thread.n"; } void thread_func() { std::call_once(flag, do_init); } int main() { std::thread t1(thread_func); std::thread t2(thread_func); std::thread t3(thread_func); t1.join(); t2.join(); t3.join(); return 0; }
尽管三个线程都调用了 std::call_once,但 do_init() 只会被执行一次,输出也只会出现一次。这是由底层互斥锁和状态检测保障的,无需程序员手动加锁判断。
相比手动加锁的优势
如果不使用 std::call_once,常见的做法是结合互斥量和布尔标志来控制初始化:
std::mutex mtx; bool initialized = false; if (!initialized) { std::lock_guard<std::mutex> lock(mtx); if (!initialized) { do_init(); initialized = true; } }
这种方式容易出错,比如忘记双重检查、死锁或内存可见性问题。而 std::call_once 内部已处理所有这些问题,语义清晰且绝对安全。
基本上就这些。std::call_once 提供了一种简洁、高效、线程安全的单次执行机制,特别适合用于初始化场景,避免竞态条件,推荐在多线程环境中优先使用。