面试题答案
一键面试内存可见性问题
内存可见性是指在多线程编程中,一个线程对共享变量的修改,何时对其他线程可见。在现代计算机系统中,为了提高性能,CPU 可能会对指令进行重排序,并且每个 CPU 核心都有自己的缓存。这就导致一个线程修改了共享变量的值,这个修改可能暂时只存在于该线程所在 CPU 核心的缓存中,还未被写回主内存,其他线程从主内存读取该变量时,就无法立即看到这个修改,从而引发内存可见性问题。
使用全局变量作为线程间通信标志可能遇到的问题
- 可见性问题:如上述内存可见性问题描述,一个线程修改了
bool flag
,其他线程可能无法及时看到这个修改,导致等待标志的线程一直处于等待状态。 - 竞态条件:多个线程同时访问和修改
flag
时,可能会出现数据竞争。例如,一个线程在读取flag
和判断flag
的值之间,另一个线程修改了flag
,可能导致逻辑错误。
使用原子操作(<atomic>
库)解决问题
<atomic>
库简介:<atomic>
库提供了一系列原子类型和操作,这些操作保证了在多线程环境下的原子性和内存可见性。原子操作是不可分割的操作,要么完全执行,要么完全不执行,不存在中间状态。- 代码示例
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<bool> flag(false);
void setFlag() {
std::this_thread::sleep_for(std::chrono::seconds(1));
flag.store(true); // 设置标志为 true
std::cout << "Flag set to true" << std::endl;
}
void waitForFlag() {
while (!flag.load()) { // 等待标志变为 true
std::this_thread::yield(); // 让出 CPU 时间片
}
std::cout << "Flag is true, continuing..." << std::endl;
}
int main() {
std::thread t1(setFlag);
std::thread t2(waitForFlag);
t1.join();
t2.join();
return 0;
}
- 代码解释
std::atomic<bool> flag(false);
:定义一个原子类型的bool
变量flag
,初始值为false
。flag.store(true);
:在setFlag
函数中,使用store
方法将flag
设置为true
。store
操作保证了修改的原子性和内存可见性,即修改会立即对其他线程可见。while (!flag.load())
:在waitForFlag
函数中,使用load
方法读取flag
的值。load
操作也保证了内存可见性,确保能读取到最新的值。std::this_thread::yield();
:在等待flag
变为true
的循环中,调用yield
方法让出 CPU 时间片,避免不必要的 CPU 消耗。
通过使用 <atomic>
库中的原子类型和操作,我们可以有效地解决多线程环境下全局变量作为通信标志时的内存可见性和竞态条件问题。