MST

星途 面试题库

面试题:C++多线程下全局变量引用的内存模型分析

在C++多线程环境中,已知一个全局变量 `std::vector<int> globalVec;`,多个线程会对其进行插入和读取操作。请详细阐述C++内存模型在这种情况下如何影响全局变量 `globalVec` 的访问一致性,以及如何通过合适的同步机制来保证数据的正确性,同时说明不同同步机制对性能的影响。
43.7万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

C++内存模型对全局变量 globalVec 访问一致性的影响

  1. 可见性问题:在多线程环境下,每个线程可能有自己的缓存。当一个线程修改了 globalVec,其他线程可能不会立即看到这个修改。这是因为C++内存模型允许编译器和处理器对内存操作进行重排序,以提高性能。例如,线程A修改了 globalVec 后,可能会先将修改暂存于缓存中,而没有立即写回主存,此时线程B从主存读取 globalVec 时,就无法获取到线程A的最新修改。
  2. 数据竞争:多个线程同时对 globalVec 进行读写操作,如果没有适当的同步,就会出现数据竞争。数据竞争会导致未定义行为,例如读取到部分修改的数据,或者修改操作相互覆盖,使得最终 globalVec 的状态不可预期。

保证数据正确性的同步机制

  1. 互斥锁(std::mutex
    • 原理:互斥锁用于保护共享资源,在同一时间只有一个线程能够获取锁并访问 globalVec。当一个线程获取到锁后,其他线程必须等待锁被释放才能获取锁并访问 globalVec
    • 示例代码
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>

std::vector<int> globalVec;
std::mutex vecMutex;

void insertData(int value) {
    std::lock_guard<std::mutex> lock(vecMutex);
    globalVec.push_back(value);
}

int readData(int index) {
    std::lock_guard<std::mutex> lock(vecMutex);
    if (index < globalVec.size()) {
        return globalVec[index];
    }
    return -1;
}
- **性能影响**:互斥锁会引入同步开销,因为线程在获取锁时可能会阻塞,这会降低系统的并发性能。如果频繁地获取和释放锁,会导致线程上下文切换频繁,增加CPU开销。

2. 读写锁(std::shared_mutex - 原理:读写锁允许多个线程同时进行读操作,但只允许一个线程进行写操作。当有线程进行写操作时,其他读写线程都必须等待。这样可以提高读操作的并发性能,因为读操作不会修改数据,不会产生数据竞争。 - 示例代码

#include <iostream>
#include <vector>
#include <thread>
#include <shared_mutex>

std::vector<int> globalVec;
std::shared_mutex vecMutex;

void insertData(int value) {
    std::unique_lock<std::shared_mutex> lock(vecMutex);
    globalVec.push_back(value);
}

int readData(int index) {
    std::shared_lock<std::shared_mutex> lock(vecMutex);
    if (index < globalVec.size()) {
        return globalVec[index];
    }
    return -1;
}
- **性能影响**:读写锁在读写操作比例不均衡的场景下有较好的性能表现。如果读操作远多于写操作,多个读线程可以同时获取共享锁进行读操作,而写操作时会独占锁,相比互斥锁,减少了读操作的等待时间,提高了并发性能。但如果写操作频繁,写操作的等待时间会增加,因为每次写操作都需要独占锁。

3. 条件变量(std::condition_variable - 原理:条件变量通常与互斥锁配合使用,用于线程间的同步通信。当某个条件满足时,通知等待的线程。例如,当 globalVec 为空时,读取线程可以等待,直到有其他线程插入数据后,通过条件变量通知读取线程。 - 示例代码

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

std::vector<int> globalVec;
std::mutex vecMutex;
std::condition_variable dataAvailable;

void insertData(int value) {
    std::unique_lock<std::mutex> lock(vecMutex);
    globalVec.push_back(value);
    lock.unlock();
    dataAvailable.notify_one();
}

int readData() {
    std::unique_lock<std::mutex> lock(vecMutex);
    dataAvailable.wait(lock, []{ return!globalVec.empty(); });
    int value = globalVec.back();
    globalVec.pop_back();
    return value;
}
- **性能影响**:条件变量本身不会直接影响性能,但如果使用不当,例如过度通知或通知不及时,可能会导致线程不必要的唤醒或等待,增加系统开销。在合理使用的情况下,它能有效地实现线程间的同步,提高整体性能。