MST

星途 面试题库

面试题:C++ 类成员函数作为回调函数的内存管理与多线程问题

当将 C++ 类成员函数作为回调函数时,如果涉及多线程场景,会面临哪些内存管理和线程安全问题?请详细分析并给出解决方案,同时假设类 `ThreadSafeClass` 有成员函数 `threadSafeCallback` 作为回调,写出相关示例代码以展示你所提出的解决方案。
13.0万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

内存管理和线程安全问题分析

  1. 内存管理问题
    • 对象生命周期管理:当类对象作为回调函数的载体时,如果对象在回调函数执行前被释放,会导致悬空指针问题。例如,在多线程环境下,一个线程创建了 ThreadSafeClass 对象并注册其成员函数作为回调,另一个线程可能在回调函数执行前意外地删除了该对象。
    • 资源竞争:如果类成员函数在回调中访问和修改类的成员变量(这些变量可能是动态分配的资源,如堆上的内存、文件句柄等),不同线程同时操作可能导致资源的不一致访问,进而出现内存泄漏或数据损坏。
  2. 线程安全问题
    • 数据竞争:多个线程同时调用 threadSafeCallback 成员函数,如果该函数访问和修改共享数据(类的成员变量),就会发生数据竞争。例如,一个线程读取成员变量,同时另一个线程修改它,可能导致读取到不一致的数据。
    • 竞态条件:当 threadSafeCallback 的执行逻辑依赖于共享数据的状态,并且多个线程可以同时改变这个状态时,可能出现竞态条件。例如,一个线程根据成员变量 flag 的值决定是否执行某些操作,另一个线程在该线程检查 flag 后但操作执行前改变了 flag 的值,可能导致程序出现错误行为。

解决方案

  1. 内存管理解决方案
    • 智能指针:使用智能指针(如 std::unique_ptrstd::shared_ptr)来管理 ThreadSafeClass 对象的生命周期。std::shared_ptr 可以确保对象在所有引用都释放后自动销毁,避免悬空指针问题。
    • 对象池:可以使用对象池技术,预先分配一定数量的 ThreadSafeClass 对象,回调函数从对象池中获取对象,使用完毕后归还对象池,这样可以更好地控制对象的生命周期。
  2. 线程安全解决方案
    • 互斥锁:在 ThreadSafeClass 中使用互斥锁(如 std::mutex)来保护共享数据。在 threadSafeCallback 函数中,在访问和修改共享数据前锁定互斥锁,操作完成后解锁。
    • 读写锁:如果共享数据的读取操作远多于写入操作,可以使用读写锁(如 std::shared_mutex)。多个线程可以同时读取共享数据,但写入操作需要独占锁,以确保数据一致性。

示例代码

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

class ThreadSafeClass {
public:
    ThreadSafeClass() : data(0) {}
    void threadSafeCallback() {
        std::lock_guard<std::mutex> lock(mutex_);
        // 模拟一些操作
        ++data;
        std::cout << "Callback executed, data: " << data << std::endl;
    }
private:
    int data;
    std::mutex mutex_;
};

void workerFunction(std::shared_ptr<ThreadSafeClass> obj) {
    for (int i = 0; i < 5; ++i) {
        obj->threadSafeCallback();
    }
}

int main() {
    std::shared_ptr<ThreadSafeClass> obj = std::make_shared<ThreadSafeClass>();
    std::thread t1(workerFunction, obj);
    std::thread t2(workerFunction, obj);

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

    return 0;
}

在上述代码中:

  • 使用 std::shared_ptr 管理 ThreadSafeClass 对象的生命周期,确保对象在所有线程使用完毕后正确销毁。
  • ThreadSafeClass 中使用 std::mutexstd::lock_guard 来保证 threadSafeCallback 函数中对共享数据 data 的线程安全访问。多个线程可以同时调用 threadSafeCallback,但对 data 的修改是线程安全的。