MST

星途 面试题库

面试题:C++类静态数据成员在多线程环境下的初始化

考虑一个多线程应用场景,类`SharedData`有一个静态数据成员`static std::vector<int> data;`。如何在多线程环境下安全地对这个静态数据成员进行初始化,以避免竞争条件?请使用C++11及以上标准库中的特性给出完整的实现代码,并解释实现原理。
24.0万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

实现代码

#include <iostream>
#include <vector>
#include <memory>
#include <mutex>
#include <thread>

class SharedData {
public:
    static std::vector<int>& getData() {
        static std::once_flag flag;
        static std::unique_ptr<std::vector<int>> data;
        std::call_once(flag, []() {
            data = std::make_unique<std::vector<int>>();
            // 在这里添加初始化逻辑,例如:
            data->push_back(1);
            data->push_back(2);
            data->push_back(3);
        });
        return *data;
    }

private:
    SharedData() = default;
    ~SharedData() = default;
    SharedData(const SharedData&) = delete;
    SharedData& operator=(const SharedData&) = delete;
};

void threadFunction() {
    std::vector<int>& data = SharedData::getData();
    std::cout << "Thread accessed data: ";
    for (int num : data) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
}

int main() {
    std::thread t1(threadFunction);
    std::thread t2(threadFunction);

    t1.join();
    t2.join();

    return 0;
}

实现原理

  1. std::once_flagstd::call_once
    • std::once_flag是一个结构体类型,用于控制std::call_once的执行。它内部维护了一个状态,用于记录关联的函数是否已经执行过。
    • std::call_once函数接受两个参数,第一个是std::once_flag对象,第二个是一个可调用对象(如函数指针、lambda表达式等)。std::call_once保证在多线程环境下,与给定的std::once_flag关联的可调用对象只被调用一次,无论有多少线程同时尝试调用它。在上述代码中,std::call_once(flag, []() { /* 初始化逻辑 */ });确保了data的初始化代码只执行一次。
  2. static局部变量
    • getData函数中,data被定义为static std::unique_ptr<std::vector<int>>类型的局部变量。这种static局部变量具有线程安全的初始化特性(C++11起)。结合std::call_once,进一步保证了data初始化的唯一性和线程安全性。
  3. 单例模式特性
    • 通过将类的构造函数、析构函数、拷贝构造函数和赋值运算符重载声明为私有或删除,确保SharedData类只能通过getData函数来访问其唯一的静态数据成员,符合单例模式的思想,从而在多线程环境下安全地提供共享数据。