MST

星途 面试题库

面试题:C++ 拷贝控制与资源管理在多线程环境下的挑战及解决方案

在多线程环境中,C++ 的拷贝控制和资源管理会面临哪些问题,比如竞争条件、死锁等。针对这些问题,分别阐述对应的解决方案,并且结合代码示例说明如何使用锁机制、原子操作等手段保证资源管理的线程安全性。
28.8万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. 多线程环境下 C++ 拷贝控制和资源管理面临的问题

  • 竞争条件:多个线程同时访问和修改共享资源(如动态分配的内存、文件描述符等),导致数据不一致。例如,两个线程同时对同一个动态分配的对象进行拷贝构造或赋值操作,可能会导致内存释放两次或数据损坏。
  • 死锁:线程之间相互等待对方释放资源,形成死循环。例如,线程 A 持有锁 L1 并等待锁 L2,而线程 B 持有锁 L2 并等待锁 A,此时就会发生死锁。

2. 解决方案

  • 锁机制
    • 互斥锁(std::mutex:用于保护共享资源,同一时间只有一个线程能获取锁并访问资源。
    • 读写锁(std::shared_mutex:允许多个线程同时进行读操作,但写操作时必须独占。适用于读多写少的场景。
  • 原子操作:对基本数据类型(如 std::atomic<int>)的操作是原子的,不会被线程调度打断,避免竞争条件。适用于简单数据类型的无锁并发访问。

3. 代码示例

  • 使用互斥锁保证资源管理的线程安全性
#include <iostream>
#include <mutex>
#include <thread>

class Resource {
public:
    Resource() : data(0) {}
    Resource(const Resource& other) {
        std::lock_guard<std::mutex> lock(other.mutex_);
        data = other.data;
    }
    Resource& operator=(const Resource& other) {
        if (this != &other) {
            std::lock_guard<std::mutex> lock(other.mutex_);
            data = other.data;
        }
        return *this;
    }
    ~Resource() {}

    void increment() {
        std::lock_guard<std::mutex> lock(mutex_);
        ++data;
    }
    int get() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return data;
    }

private:
    int data;
    mutable std::mutex mutex_;
};

void threadFunction(Resource& res) {
    for (int i = 0; i < 1000; ++i) {
        res.increment();
    }
}

int main() {
    Resource res;
    std::thread t1(threadFunction, std::ref(res));
    std::thread t2(threadFunction, std::ref(res));

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

    std::cout << "Final value: " << res.get() << std::endl;
    return 0;
}

在上述代码中,Resource 类使用 std::mutex 来保护数据成员 data。在拷贝构造、赋值运算符和数据修改成员函数中,通过 std::lock_guard 自动管理锁的获取和释放,确保线程安全。

  • 使用原子操作保证简单数据类型的线程安全性
#include <iostream>
#include <atomic>
#include <thread>

std::atomic<int> atomicData(0);

void atomicIncrement() {
    for (int i = 0; i < 1000; ++i) {
        atomicData++;
    }
}

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

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

    std::cout << "Atomic final value: " << atomicData << std::endl;
    return 0;
}

此代码中,std::atomic<int> 类型的 atomicData 确保了自增操作的原子性,无需额外的锁,从而避免竞争条件。