MST
星途 面试题库

面试题:C++函数返回常量引用在多线程环境下的优化与问题探讨

在多线程的C++程序中,有一个函数返回一个共享资源的常量引用。分析这种情况下可能出现的线程安全问题,从内存可见性、竞态条件等角度进行阐述。同时,提出至少两种有效的解决方案,并说明每种方案的优缺点以及适用场景。
41.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

可能出现的线程安全问题

  1. 内存可见性:不同线程可能缓存共享资源的不同副本。当一个线程修改了共享资源,其他线程可能不会立即看到修改后的值,导致程序逻辑错误。例如,一个线程更新了共享资源的某个字段,另一个线程获取该共享资源的常量引用后,却仍然使用旧的值。
  2. 竞态条件:多个线程同时访问和修改共享资源,其最终结果依赖于线程执行的相对顺序。即使函数返回常量引用,共享资源本身可能在其他地方被修改。比如,在获取常量引用之前,共享资源可能正处于部分修改的状态,这就导致获取到的引用指向一个不一致的状态。

解决方案

  1. 互斥锁(Mutex)
    • 实现方式:在访问共享资源前加锁,访问完成后解锁。在返回共享资源的常量引用的函数中,使用互斥锁保护共享资源的访问。
    • 优点:简单直接,适用于大多数场景,能有效避免竞态条件和保证内存可见性。
    • 缺点:加锁和解锁操作会带来性能开销,尤其是在高并发情况下,可能导致线程频繁等待锁,降低系统整体性能。
    • 适用场景:适用于对性能要求不是极高,共享资源访问频率不是特别频繁的场景。

示例代码:

#include <iostream>
#include <mutex>

std::mutex mtx;
class SharedResource {
public:
    int data;
    SharedResource() : data(0) {}
};

SharedResource shared;

const SharedResource& getSharedResource() {
    std::lock_guard<std::mutex> lock(mtx);
    return shared;
}
  1. 读写锁(Read - Write Lock)
    • 实现方式:允许多个线程同时进行读操作(获取常量引用属于读操作),但只允许一个线程进行写操作。写操作时,其他读和写操作都要等待。
    • 优点:读操作并发性能好,适用于读多写少的场景,能有效提高系统性能。因为读操作不修改共享资源,多个线程同时读不会产生竞态条件,所以可以并行执行。
    • 缺点:实现相对复杂,写操作的同步开销较大,因为每次写操作都需要独占锁,可能导致读线程等待。
    • 适用场景:适用于读操作远远多于写操作的场景,例如配置文件读取等场景,配置文件更新频率低,但读取频率高。

示例代码(以C++17的std::shared_mutex为例):

#include <iostream>
#include <shared_mutex>

std::shared_mutex rw_mtx;
class SharedResource {
public:
    int data;
    SharedResource() : data(0) {}
};

SharedResource shared;

const SharedResource& getSharedResource() {
    std::shared_lock<std::shared_mutex> lock(rw_mtx);
    return shared;
}
  1. 线程本地存储(Thread - Local Storage,TLS)
    • 实现方式:为每个线程提供一份独立的共享资源副本,这样每个线程对共享资源的访问都是线程本地的,不存在线程间的竞争。
    • 优点:彻底避免了线程间的竞争,性能非常好,因为不需要锁操作。
    • 缺点:如果共享资源需要在多个线程间保持一致性,TLS就不适用了。而且,会增加内存消耗,因为每个线程都有一份副本。
    • 适用场景:适用于共享资源不需要在多个线程间保持一致,每个线程独立使用的场景,比如日志记录,每个线程记录自己的日志,不需要与其他线程同步。

示例代码(以C++的__thread关键字为例,不同编译器可能有不同实现):

#include <iostream>

__thread int localData;

void someFunction() {
    localData = 42;
    std::cout << "Thread - local data: " << localData << std::endl;
}