面试题答案
一键面试可能出现的内存安全问题
在多线程并发调用updateSharedVar
和readSharedVar
时,可能出现以下内存安全问题:
- 数据竞争(Data Race):多个线程同时访问和修改
sharedVar
,导致最终结果不可预测。例如,线程A读取sharedVar
的值,在它还未修改并写回之前,线程B也读取了同样的值,这样线程A的修改就被线程B覆盖,造成数据不一致。
解决方案
- 互斥锁(Mutex)
- 实现原理:使用互斥锁(如C++中的
std::mutex
)来保护对sharedVar
的访问。在进入updateSharedVar
和readSharedVar
函数时,线程首先获取互斥锁,离开函数时释放互斥锁。这样同一时间只有一个线程能访问sharedVar
,避免数据竞争。 - 优点:实现简单直观,适用于大多数场景,能有效解决数据竞争问题。
- 缺点:性能开销较大,因为线程获取和释放锁需要一定时间。如果锁的粒度较大(保护的代码块较长),会导致其他线程等待时间过长,降低并发性能。而且如果使用不当,可能会出现死锁。
- 实现原理:使用互斥锁(如C++中的
- 读写锁(Read - Write Lock)
- 实现原理:读写锁区分了读操作和写操作。允许多个线程同时进行读操作,因为读操作不会修改数据,不会引发数据竞争。但是写操作时,需要独占锁,防止其他线程进行读或写操作。例如在C++中可以使用
std::shared_mutex
,读线程获取共享锁(lock_shared
),写线程获取独占锁(lock
)。 - 优点:适合读多写少的场景,能提高并发性能。读操作并发执行,减少了线程等待时间。
- 缺点:实现相对复杂。如果写操作过于频繁,读线程可能会长时间等待,导致性能下降。同时,使用不当也可能出现死锁情况。
- 实现原理:读写锁区分了读操作和写操作。允许多个线程同时进行读操作,因为读操作不会修改数据,不会引发数据竞争。但是写操作时,需要独占锁,防止其他线程进行读或写操作。例如在C++中可以使用
- 原子操作(Atomic Operations)
- 实现原理:将
sharedVar
声明为原子类型(如C++中的std::atomic<int>
)。原子类型的操作是不可分割的,在多线程环境下自动保证操作的原子性,无需额外的锁机制。例如,std::atomic<int>
的自增操作++
是原子的,不会出现数据竞争。 - 优点:性能开销小,尤其适用于简单的变量操作。不需要显式的锁,避免了锁带来的开销和死锁风险。
- 缺点:只适用于简单的原子操作,对于复杂的操作(如多个原子变量之间的组合操作),仍然需要锁或其他同步机制。而且原子类型的功能相对有限,不像普通变量那样灵活。
- 实现原理:将