MST
星途 面试题库

面试题:网络编程中多线程编程的内存模型与可见性问题 - 高级难度

在C++的网络多线程程序里,线程A向网络发送数据后修改一个共享的标志位,线程B在检测到该标志位变化后进行后续数据处理。然而实际运行中,线程B有时检测不到标志位的变化。请分析可能导致该问题的原因,以及如何通过C++ 11的原子操作(std::atomic)来解决该问题,并给出代码示例。
46.4万 热度难度
后端开发网络编程

知识考点

AI 面试

面试题答案

一键面试

可能原因分析

  1. 缓存一致性问题:现代CPU为提高性能,每个核心都有自己的缓存。线程A修改标志位后,该修改可能只存在于其所在核心的缓存中,未及时刷新到主存,导致线程B从主存读取标志位时,读到的还是旧值。
  2. 编译器优化:编译器可能对代码进行优化,将标志位的读取操作优化到循环外部,从而导致线程B检测不到标志位的变化。

使用std::atomic解决问题

std::atomic提供了原子操作,保证对其变量的读写操作是原子性的,并且可以通过指定内存顺序来解决缓存一致性和编译器优化带来的问题。

代码示例

#include <iostream>
#include <thread>
#include <atomic>
#include <chrono>

std::atomic<bool> flag(false);

void threadA() {
    // 模拟向网络发送数据
    std::this_thread::sleep_for(std::chrono::seconds(1));
    flag.store(true, std::memory_order_release);
}

void threadB() {
    while (!flag.load(std::memory_order_acquire)) {
        std::this_thread::yield();
    }
    // 后续数据处理
    std::cout << "Thread B detected flag change and start processing." << std::endl;
}

int main() {
    std::thread t1(threadA);
    std::thread t2(threadB);

    t1.join();
    t2.join();

    return 0;
}

在上述代码中:

  • std::atomic<bool> flag(false);定义了一个原子类型的标志位flag
  • flag.store(true, std::memory_order_release);在线程A中使用store方法设置标志位为true,并使用std::memory_order_release内存顺序,保证在此之前的写操作对其他线程可见。
  • while (!flag.load(std::memory_order_acquire)) { }在线程B中使用load方法读取标志位,并使用std::memory_order_acquire内存顺序,保证在此之后的读操作能看到之前线程A的写操作。