MST

星途 面试题库

面试题:C++ 函数返回引用的异常安全性在多线程环境下的考量

假设在多线程环境中,有一个函数 `MyClass& sharedGetMyClass()` 会返回 `MyClass` 实例的引用,该实例是多个线程共享的。这个函数内部可能会进行复杂的初始化操作并可能抛出异常。请讨论在这种场景下异常安全性面临的新挑战,如何通过同步机制(如 `std::mutex` 等)和异常安全的设计原则来保证在多线程环境下,函数返回的引用始终指向一个处于有效状态的 `MyClass` 对象,并且不会因为异常导致数据竞争或资源泄漏。请用伪代码或实际代码示例阐述关键实现点。
24.0万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

异常安全性面临的挑战

  1. 数据竞争:多个线程同时调用 sharedGetMyClass() 时,可能会导致复杂初始化操作的竞态条件,例如部分初始化完成但未完全构造好对象就被其他线程使用,进而引发未定义行为。
  2. 资源泄漏:在初始化过程中,如果抛出异常,可能会导致已分配的资源(如内存、文件句柄等)无法正确释放,特别是在多线程环境下,这种情况更难排查和处理。

解决方案

  1. 同步机制:使用 std::mutex 来保护共享资源和初始化操作,确保同一时间只有一个线程能够进行初始化。
  2. 异常安全设计原则:采用 RAII(Resource Acquisition Is Initialization)机制来管理资源,确保资源在对象生命周期结束时自动释放,避免资源泄漏。

代码示例(C++)

#include <memory>
#include <mutex>
#include <iostream>

class MyClass {
public:
    MyClass() {
        std::cout << "MyClass constructor" << std::endl;
        // 模拟复杂初始化操作,可能抛出异常
        if (rand() % 2) {
            throw std::runtime_error("Initialization failed");
        }
    }
    ~MyClass() {
        std::cout << "MyClass destructor" << std::endl;
    }
};

std::mutex myMutex;
std::unique_ptr<MyClass> sharedMyClass;

MyClass& sharedGetMyClass() {
    static std::once_flag onceFlag;
    std::call_once(onceFlag, []() {
        std::lock_guard<std::mutex> lock(myMutex);
        try {
            sharedMyClass.reset(new MyClass());
        } catch (...) {
            // 处理异常,确保不会泄漏资源
            std::cerr << "Exception caught during MyClass initialization" << std::endl;
            throw;
        }
    });
    return *sharedMyClass;
}

关键实现点解释

  1. std::once_flagstd::call_oncestd::once_flag 用于标记某个函数是否已经执行过一次,std::call_once 确保其关联的函数只被调用一次,无论有多少线程同时尝试调用。这避免了重复初始化的问题。
  2. std::lock_guard:在 std::call_once 内部的初始化函数中,使用 std::lock_guard 来锁定 std::mutexstd::lock_guard 采用 RAII 机制,在构造时自动锁定互斥锁,在析构时自动解锁,确保互斥锁的正确管理。
  3. 异常处理:在初始化 MyClass 对象时,如果抛出异常,catch 块会捕获异常,输出错误信息,并重新抛出异常,确保函数调用者能够处理异常,同时保证资源不会泄漏。因为 std::unique_ptr 会在 MyClass 对象构造失败时自动释放已分配的内存。

通过以上同步机制和异常安全设计原则,可以保证在多线程环境下,sharedGetMyClass() 函数返回的引用始终指向一个处于有效状态的 MyClass 对象,并且不会因为异常导致数据竞争或资源泄漏。