面试题答案
一键面试C++内存模型在多线程环境下的工作原理
C++内存模型定义了多线程访问内存时的规则,确保不同线程间的内存访问操作有一致的语义。它规定了一个线程对内存的写入何时对其他线程可见。例如,当一个线程修改了某个变量的值,内存模型决定了其他线程何时能看到这个修改。
数据竞争问题举例
#include <iostream>
#include <thread>
int shared_variable = 0;
void increment() {
for (int i = 0; i < 10000; ++i) {
shared_variable++;
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final value: " << shared_variable << std::endl;
// 预期结果应为20000,但由于数据竞争,实际结果可能小于20000
return 0;
}
在上述代码中,两个线程同时对shared_variable
进行自增操作。由于shared_variable++
不是原子操作,它包含读取、增加和写入三个步骤,不同线程的这些步骤可能交错执行,导致数据竞争,最终结果可能小于预期的20000。
利用原子操作和内存序避免数据竞争
原子操作
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> shared_variable(0);
void increment() {
for (int i = 0; i < 10000; ++i) {
shared_variable++;
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final value: " << shared_variable << std::endl;
// 此时结果应为20000
return 0;
}
使用std::atomic<int>
类型,shared_variable++
操作变为原子操作,避免了数据竞争。
内存序
内存序决定了原子操作与其他内存操作之间的顺序关系。例如std::memory_order_seq_cst
(顺序一致性内存序),它保证所有线程以相同顺序看到所有原子操作,这是最严格的内存序。
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<bool> ready(false);
std::atomic<int> data(0);
void producer() {
data.store(42, std::memory_order_seq_cst);
ready.store(true, std::memory_order_seq_cst);
}
void consumer() {
while (!ready.load(std::memory_order_seq_cst));
std::cout << "Data: " << data.load(std::memory_order_seq_cst) << std::endl;
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
在这个例子中,使用std::memory_order_seq_cst
保证了producer
线程先存储数据,再设置ready
为true
,consumer
线程先读取ready
,再读取data
,确保数据的一致性。
不同内存序对程序性能和正确性的影响
性能影响
- 顺序一致性内存序(
std::memory_order_seq_cst
):最严格,性能开销最大。因为它要求所有线程对原子操作有全局一致的顺序,需要更多的同步操作。 - 释放 - 获取内存序(
std::memory_order_release
和std::memory_order_acquire
):性能较好。std::memory_order_release
在释放操作时,确保之前的写操作对其他获取该变量的线程可见;std::memory_order_acquire
在获取操作时,确保之后的读操作能看到之前释放操作的结果。 - 松弛内存序(如
std::memory_order_relaxed
):性能最优,因为它仅保证原子操作自身的原子性,不保证任何内存顺序,可能导致数据竞争,仅适用于一些特殊场景,如计数。
正确性影响
- 越严格的内存序(如
std::memory_order_seq_cst
)越能保证程序正确性,但可能牺牲性能。 - 较宽松的内存序(如
std::memory_order_relaxed
)如果使用不当,会导致数据竞争,破坏程序正确性。在选择内存序时,需要在性能和正确性之间权衡,根据程序的具体需求选择合适的内存序。