C++11内存模型改进内容
- 原子操作:
- C++11引入了
<atomic>
头文件,定义了一系列原子类型和原子操作。原子类型(如std::atomic<int>
)保证对其的操作是不可分割的,在多线程环境下不会出现数据竞争。例如,std::atomic<int> counter;
,对counter
的++
操作在多线程中是原子的,不需要额外的锁。
- 原子操作支持多种内存顺序(memory order),包括
std::memory_order_seq_cst
(顺序一致性内存序)、std::memory_order_release
、std::memory_order_acquire
等。
- 内存顺序:
- 顺序一致性内存序(
std::memory_order_seq_cst
):这是最严格的内存序,所有线程对原子操作的执行顺序是一致的,就好像存在一个全局的顺序。例如,在多个线程对同一个std::atomic<int>
变量进行读写操作时,按照顺序一致性内存序,所有线程看到的操作顺序都是一样的。
- 释放 - 获取内存序(
std::memory_order_release
和std::memory_order_acquire
):std::memory_order_release
用于写操作,它保证所有之前的写操作对其他线程可见;std::memory_order_acquire
用于读操作,它保证后续的读操作能看到之前线程释放的写操作结果。例如,一个线程在写一个共享变量时使用std::memory_order_release
,另一个线程在读这个变量时使用std::memory_order_acquire
,就能保证读线程能看到写线程对该变量以及之前相关变量的修改。
- 线程局部存储(TLS):
- C++11通过
thread_local
关键字支持线程局部存储。每个线程都有自己独立的thread_local
变量副本,不同线程对thread_local
变量的修改不会相互影响。例如,thread_local int local_value;
,每个线程都有自己的local_value
,保证了数据的独立性。
在多线程编程场景下保证数据一致性和线程安全的方式
- 原子操作保证数据一致性:通过原子类型和原子操作,避免了多线程同时访问和修改同一数据时的数据竞争。例如,多个线程对
std::atomic<int>
类型的计数器进行自增操作,由于操作的原子性,不会出现计数器值错误的情况,保证了数据一致性。
- 内存顺序保证线程安全:合理使用不同的内存顺序可以确保线程之间数据的正确同步。例如,在生产者 - 消费者模型中,生产者线程在向共享缓冲区写入数据后使用
std::memory_order_release
,消费者线程在读取数据前使用std::memory_order_acquire
,这样就能保证消费者线程能读取到生产者线程正确写入的数据,保证了线程安全。
- 线程局部存储保证线程安全:
thread_local
变量为每个线程提供独立的数据副本,不同线程对其操作不会相互干扰,从而保证了线程安全。例如,在多线程日志记录场景下,每个线程的日志数据可以存储在thread_local
变量中,避免了线程间日志数据的冲突。
应用场景举例
- 计数器应用场景:在多线程并发访问的服务器中,需要统计请求的数量。可以使用
std::atomic<int>
类型的计数器,每个线程在处理请求时对计数器进行原子自增操作,保证计数器值的准确性。
#include <iostream>
#include <atomic>
#include <thread>
std::atomic<int> counter(0);
void increment() {
for (int i = 0; i < 1000; ++i) {
counter++;
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final counter value: " << counter << std::endl;
return 0;
}
- 生产者 - 消费者模型:在一个多线程的数据处理系统中,生产者线程不断生成数据并放入共享缓冲区,消费者线程从共享缓冲区取出数据进行处理。通过使用合适的内存顺序(如生产者使用
std::memory_order_release
,消费者使用std::memory_order_acquire
),可以保证数据的正确传递和处理。
#include <iostream>
#include <atomic>
#include <thread>
#include <queue>
std::atomic<bool> data_ready(false);
std::queue<int> data_queue;
void producer() {
for (int i = 0; i < 10; ++i) {
data_queue.push(i);
data_ready.store(true, std::memory_order_release);
}
}
void consumer() {
while (true) {
if (data_ready.load(std::memory_order_acquire)) {
while (!data_queue.empty()) {
int data = data_queue.front();
data_queue.pop();
std::cout << "Consumed: " << data << std::endl;
}
data_ready.store(false, std::memory_order_release);
}
}
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
- 多线程日志记录:在一个多线程的应用程序中,每个线程需要记录自己的日志信息。可以使用
thread_local
变量来存储每个线程的日志缓冲区,避免线程间日志数据的冲突。
#include <iostream>
#include <thread>
thread_local std::string thread_log;
void log_message(const std::string& message) {
thread_log += message + "\n";
}
void thread_function() {
log_message("Thread started");
// 线程执行其他操作
log_message("Thread finished");
std::cout << "Thread log:\n" << thread_log << std::endl;
}
int main() {
std::thread t1(thread_function);
std::thread t2(thread_function);
t1.join();
t2.join();
return 0;
}