MST

星途 面试题库

面试题:C++私有成员变量可访问函数与多线程及内存安全

在多线程环境下,一个类`SharedData`包含私有成员变量`sharedVar`以及用于访问它的成员函数`updateSharedVar`和`readSharedVar`。请分析在多线程并发调用这些函数时可能出现的内存安全问题,并给出至少两种有效的解决方案,详细说明每种方案的实现原理以及优缺点。
27.2万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

可能出现的内存安全问题

在多线程并发调用updateSharedVarreadSharedVar时,可能出现以下内存安全问题:

  1. 数据竞争(Data Race):多个线程同时访问和修改sharedVar,导致最终结果不可预测。例如,线程A读取sharedVar的值,在它还未修改并写回之前,线程B也读取了同样的值,这样线程A的修改就被线程B覆盖,造成数据不一致。

解决方案

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