面试题答案
一键面试潜在问题
- 缓存一致性问题:不同线程可能将数据缓存在各自的处理器缓存中。当一个线程修改了数据(即使数据本应是只读的,但如果没有正确的内存屏障等机制,缓存可能不一致),其他线程可能无法及时看到最新的值。
- 指令重排问题:编译器和处理器为了优化性能,可能会对指令进行重排。在多线程环境下,这可能导致线程看到的数据顺序与实际写入顺序不一致,影响数据一致性。
技术手段确保数据一致性和性能优化
- 使用内存模型:
- 在 C++11 中,
std::memory_order
提供了不同的内存模型。例如,std::memory_order_seq_cst
提供了顺序一致性模型,所有线程都以相同的顺序看到所有修改。但它性能相对较低,因为它禁止了较多的指令重排。对于只读数据,如果能确定不会被修改,可以使用更宽松的内存模型,如std::memory_order_acquire
和std::memory_order_release
组合使用。 - 示例代码:
- 在 C++11 中,
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> data(0);
std::atomic<bool> ready(false);
void producer() {
data.store(42, std::memory_order_release);
ready.store(true, std::memory_order_release);
}
void consumer() {
while (!ready.load(std::memory_order_acquire));
std::cout << "Data: " << data.load(std::memory_order_acquire) << std::endl;
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
- 同步机制:
- 互斥锁(Mutex):使用
std::mutex
可以保护共享数据。当一个线程访问数据时,它需要先锁定互斥锁,其他线程必须等待锁被释放才能访问数据。 - 示例代码:
- 互斥锁(Mutex):使用
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
const int& sharedData = 42;
void printData() {
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Shared data: " << sharedData << std::endl;
}
int main() {
std::thread t1(printData);
std::thread t2(printData);
t1.join();
t2.join();
return 0;
}
- 读写锁(Read - Write Lock):对于只读数据,可以使用读写锁,允许多个线程同时读数据,但写数据时需要独占锁。在 C++ 中,可以使用
std::shared_mutex
实现。 - 示例代码:
#include <iostream>
#include <thread>
#include <shared_mutex>
std::shared_mutex rwMutex;
const int& sharedData = 42;
void readData() {
std::shared_lock<std::shared_mutex> lock(rwMutex);
std::cout << "Read data: " << sharedData << std::endl;
}
void writeData() {
std::unique_lock<std::shared_mutex> lock(rwMutex);
// 这里如果 sharedData 真的是常量,就不应该在这里尝试修改,但为了展示写锁的使用逻辑
// 实际应该有个可修改的变量
std::cout << "Write operation (not really modifying constant here)" << std::endl;
}
int main() {
std::thread t1(readData);
std::thread t2(readData);
std::thread t3(writeData);
t1.join();
t2.join();
t3.join();
return 0;
}
通过这些内存模型和同步机制,可以在多线程环境中确保只读数据的一致性,并在一定程度上优化性能。