如何在多线程环境下使call_once()在构造函数中正确工作?
在构造函数中使用std::call_once的正确姿势
我之前专门在多线程环境下测试过std::call_once()对普通变量的控制,实际行为完全符合预期——确实能保证目标函数只被执行一次。但在构造函数里想用它实现延迟初始化的时候,踩了不少坑,折腾了挺久才摸清楚正确的用法。
下面是我基于Stroustrup《C++程序设计语言》第4版的示例修改后的代码,文件名是initialize_once_class_ctor.cpp,专门用来演示如何在构造函数场景下正确使用std::once_flag和std::call_once()完成延迟初始化:
#include <iostream> #include <mutex> #include <thread> #include <chrono> class DelayedInitClass { private: // 必须是静态成员,确保所有实例共享同一个初始化标记 static std::once_flag init_flag; // 需要延迟初始化的共享资源 static int shared_data; // 实际执行初始化的私有函数 void do_init() { // 模拟耗时的初始化操作,比如加载配置、创建全局资源等 std::this_thread::sleep_for(std::chrono::milliseconds(200)); shared_data = 1024; std::cout << "Shared resource initialized by thread ID: " << std::this_thread::get_id() << std::endl; } public: DelayedInitClass() { // 在构造函数中调用call_once,确保初始化只执行一次 // 注意:如果是成员函数作为回调,需要传入this指针作为额外参数 std::call_once(init_flag, &DelayedInitClass::do_init, this); } // 提供外部访问共享资源的接口 void use_shared_data() { std::cout << "Thread " << std::this_thread::get_id() << " accessed shared data: " << shared_data << std::endl; } }; // 静态成员变量的类外初始化 std::once_flag DelayedInitClass::init_flag; int DelayedInitClass::shared_data = 0; int main() { const int thread_num = 6; std::thread threads[thread_num]; // 创建多个线程,每个线程都构造类实例 for (int i = 0; i < thread_num; ++i) { threads[i] = std::thread([](){ DelayedInitClass instance; instance.use_shared_data(); }); } // 等待所有线程完成 for (auto& t : threads) { t.join(); } return 0; }
关键注意事项:
std::once_flag必须声明为静态成员变量:如果是普通成员,每个类实例都会有自己的flag,根本起不到全局控制初始化次数的作用,这是最容易踩的坑- 如果初始化函数是类的非静态成员函数,调用
std::call_once时必须传递this指针作为额外参数,否则无法正确调用成员函数 - 即使多个线程同时进入构造函数,
std::call_once也会保证初始化函数仅被执行一次,完全避免了多线程下的竞态问题
内容的提问来源于stack exchange,提问作者SSteven
相关产品推荐
相关产品推荐

