面试题答案
一键面试C++内存模型对全局变量 globalVec
访问一致性的影响
- 可见性问题:在多线程环境下,每个线程可能有自己的缓存。当一个线程修改了
globalVec
,其他线程可能不会立即看到这个修改。这是因为C++内存模型允许编译器和处理器对内存操作进行重排序,以提高性能。例如,线程A修改了globalVec
后,可能会先将修改暂存于缓存中,而没有立即写回主存,此时线程B从主存读取globalVec
时,就无法获取到线程A的最新修改。 - 数据竞争:多个线程同时对
globalVec
进行读写操作,如果没有适当的同步,就会出现数据竞争。数据竞争会导致未定义行为,例如读取到部分修改的数据,或者修改操作相互覆盖,使得最终globalVec
的状态不可预期。
保证数据正确性的同步机制
- 互斥锁(
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;
}
- **性能影响**:条件变量本身不会直接影响性能,但如果使用不当,例如过度通知或通知不及时,可能会导致线程不必要的唤醒或等待,增加系统开销。在合理使用的情况下,它能有效地实现线程间的同步,提高整体性能。