面试题答案
一键面试合理引用全局变量的方式及技术分析
-
互斥锁(
std::mutex
)- 使用方法:在访问和修改
globalVector
前后加锁和解锁。 - 优点:
- 简单直观,容易理解和实现。
- 能有效保证数据一致性,避免竞态条件。
- 缺点:
- 性能开销较大,加锁和解锁操作会引入额外的时间消耗,在高并发场景下可能成为性能瓶颈。
- 可能导致死锁,若加锁顺序不当。
- 使用方法:在访问和修改
-
读写锁(
std::shared_mutex
)- 使用方法:读操作使用共享锁,写操作使用独占锁。
- 优点:
- 适用于读多写少的场景,读操作可以并发执行,提高性能。
- 依然能保证数据一致性。
- 缺点:
- 实现相对复杂,需要区分读锁和写锁的使用。
- 写操作时依然会阻塞其他所有操作,在写操作频繁时性能提升有限。
-
原子操作
- 使用方法:对于
std::vector<int>
这种复杂类型,原子操作直接应用有限,但可以原子操作vector
的大小等简单属性。 - 优点:
- 无锁操作,避免了锁带来的开销和死锁风险。
- 对于简单数据类型的操作性能较高。
- 缺点:
- 不适用于复杂数据结构的整体操作,如对
std::vector<int>
元素的复杂修改。 - 实现复杂,需要对底层硬件和原子指令有深入了解。
- 不适用于复杂数据结构的整体操作,如对
- 使用方法:对于
底层问题及解决方案
-
内存对齐
- 问题:不同硬件平台对内存对齐要求不同,如果数据未按要求对齐,可能导致性能下降甚至硬件异常。
- 解决方案:使用编译器特定的指令或属性来指定内存对齐,如
#pragma pack
或alignas
关键字。在C++ 中,std::vector
通常会自动处理好内存对齐问题,但如果对vector
中的元素类型有特殊对齐要求,需额外处理。
-
缓存一致性
- 问题:多线程环境下,不同CPU核心的缓存可能不一致,导致读取到旧数据。
- 解决方案:使用内存屏障(
std::memory_order
)或锁机制,保证数据修改对所有线程可见。锁操作会隐式包含内存屏障的功能,原子操作也可以通过设置合适的std::memory_order
来确保缓存一致性。
多线程示例代码
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
std::vector<int> globalVector;
std::mutex globalMutex;
void addElement(int num) {
std::lock_guard<std::mutex> lock(globalMutex);
globalVector.push_back(num);
}
void printVector() {
std::lock_guard<std::mutex> lock(globalMutex);
for (int num : globalVector) {
std::cout << num << " ";
}
std::cout << std::endl;
}
int main() {
std::thread t1(addElement, 1);
std::thread t2(addElement, 2);
std::thread t3(printVector);
t1.join();
t2.join();
t3.join();
return 0;
}
此代码通过 std::mutex
保证了对 globalVector
的安全访问,在多线程环境下实现了数据的一致性。在实际复杂场景中,可根据读/写操作的比例选择更合适的同步机制,如读写锁。