面试题答案
一键面试措施
- 互斥锁(
std::mutex
)- 使用方式:在对
globalVec
进行读写操作前,先锁定互斥锁,操作完成后解锁。例如:
std::mutex globalVecMutex; void writeToGlobalVec(int value) { std::lock_guard<std::mutex> lock(globalVecMutex); globalVec.push_back(value); } int readFromGlobalVec(size_t index) { std::lock_guard<std::mutex> lock(globalVecMutex); return globalVec[index]; }
- 使用方式:在对
- 读写锁(
std::shared_mutex
C++17 及以上)- 使用方式:对于读操作,使用共享锁(
std::shared_lock
);对于写操作,使用独占锁(std::unique_lock
)。例如:
std::shared_mutex globalVecReadWriteMutex; void writeToGlobalVec(int value) { std::unique_lock<std::shared_mutex> lock(globalVecReadWriteMutex); globalVec.push_back(value); } int readFromGlobalVec(size_t index) { std::shared_lock<std::shared_mutex> lock(globalVecReadWriteMutex); return globalVec[index]; }
- 使用方式:对于读操作,使用共享锁(
- 原子操作(
std::atomic
部分场景适用)- 使用方式:如果
globalVec
的操作主要是简单的追加或特定原子性支持的操作,可以考虑使用std::atomic
相关特性。例如,如果只是简单地追加元素,可以自定义一个基于std::atomic<int>
的索引来实现线程安全的追加(但这种方式对复杂的vector
操作支持有限)。
std::atomic<int> globalVecIndex(0); void writeToGlobalVec(int value) { int idx = globalVecIndex.fetch_add(1); globalVec[idx] = value; }
- 使用方式:如果
性能和复杂性差异
- 互斥锁
- 性能:读写操作都需要获取独占锁,当有多个读操作时,即使它们之间不会相互影响,也会因为锁的独占性而串行化,性能相对较低,尤其是读操作频繁的场景。
- 复杂性:实现相对简单,只需要一个互斥锁对象,在需要保护的代码段前后进行加锁和解锁操作。
- 读写锁
- 性能:读操作可以并发执行,因为读锁是共享的,只有写操作需要独占锁。所以在多读少写的场景下,性能优于互斥锁。但在多写场景下,与互斥锁性能相近,因为写操作都需要独占锁。
- 复杂性:比互斥锁略复杂,需要区分读锁(
std::shared_lock
)和写锁(std::unique_lock
)的使用,并且需要理解共享锁和独占锁的机制。
- 原子操作
- 性能:在特定操作下(如简单的原子性计数等)性能非常高,因为不需要像锁那样进行上下文切换等开销。但对于复杂的
vector
操作,如插入、删除元素等,原子操作难以满足需求,并且可能需要复杂的设计来模拟完整的vector
行为。 - 复杂性:实现复杂,对于
std::vector
这样复杂的数据结构,要使用原子操作保证线程安全,需要深入理解原子操作的原理和对vector
底层机制的了解,并且往往只能实现部分功能。
- 性能:在特定操作下(如简单的原子性计数等)性能非常高,因为不需要像锁那样进行上下文切换等开销。但对于复杂的