C++ 并发编程系列笔记,ch5笔记
C++ 并发笔记
Created 2021.02.05 by William Yu; Last modified: 2022.09.16-v1.1.0
Contact: windmillyucong@163.com
Copyleft! 2022 William Yu. Some rights reserved.
Chapter Five: 内存模型与原子操作
References
- https://www.kancloud.cn/jxm_zn/cpp_concurrency_in_action/264956
5.0 Concepts
-
内存模型
-
原子类型
-
原子操作
5.1 内存模型
5.1.1 对象的存储形式
- 每一个变量都是一个对象,包括成员变量的对象
- 每个对象至少占有一个内存位置
- 基本类型都有确定的内存位置
- 相邻位域是相同内存中的一部分
5.1.2 考虑并发
- 所有东西都在内存中。
- 当两个线程访问不同(separate)的内存位置时,不会存在任何问题,一切都工作顺利。
- 而当两个线程访问同一(same)个内存位置,你就要小心了。
- 如果没有线程更新内存位置上的数据,那还好;只读数据不需要保护或同步。
- 当有线程对内存位置上的数据进行修改,那就有可能会产生条件竞争,
5.2 原子操作与原子类型
5.1.1 原子操作
- 原子操作:一类不可分割的操作,
- 当这样操作在任意线程中进行一半的时候,你是不能查看的
- 它的状态要不就是完成,要不就是未完成
- 注意:加锁不加锁的前提是看能不能cpu一条指令读取到
- CPU 同内存交换数据的最小内存单位为4字节
- 因此小于 4 字节的无需加锁, 如 int, char。
- 而大于4 字节的, 如 double, 64位的 long 需要加锁。
- 以上只是对其读写不需加锁, 对 i++ 这类组合操作,仍需加锁。
- 非原子操作:
- 可能会被视为由一个线程完成一半的操作
- 如果这种是一个存储操作,那么其他线程看到的,可能既不是存储前的值,也可能不是已存储的值
5.2.2 原子类型
头文件
1
#include <atomic>
类型
原子类型 | 相关特化类 |
---|---|
atomic_bool | std::atomic |
atomic_char | std::atomic |
atomic_schar | std::atomic |
atomic_uchar | std::atomic |
atomic_int | std::atomic |
atomic_uint | std::atomic |
atomic_short | std::atomic |
atomic_ushort | std::atomic |
atomic_long | std::atomic |
atomic_ulong | std::atomic |
atomic_llong | std::atomic |
atomic_ullong | std::atomic |
atomic_char16_t | std::atomic |
atomic_char32_t | std::atomic |
atomic_wchar_t | std::atomic |
使用
1
std::atomic_int times_ = {0}; // 初始化时使用{}
使用时不用考虑锁,直接当做正常变量使用即可。
5.3 栅栏
参 https://www.kancloud.cn/jxm_zn/cpp_concurrency_in_action/264956#51__12 5.3小节
5.3.1 内存栅栏的概念
- 内存栅栏(Memory Fence)是一种同步原语,用于控制内存访问的顺序
- 主要作用:
- 防止指令重排
- 确保内存操作的可见性
- 保证多线程间的同步
5.3.2 栅栏类型
C++提供了三种内存栅栏:
- 获取栅栏(Acquire Fence)
1
std::atomic_thread_fence(std::memory_order_acquire);
- 确保栅栏后的所有读操作不会被重排到栅栏之前
- 用于读取共享数据时
- 释放栅栏(Release Fence)
1
std::atomic_thread_fence(std::memory_order_release);
- 确保栅栏前的所有写操作不会被重排到栅栏之后
- 用于写入共享数据时
- 全栅栏(Full Fence)
1
std::atomic_thread_fence(std::memory_order_seq_cst);
- 最严格的内存序
- 确保栅栏前后的所有操作都不会被重排
- 保证所有线程看到相同的操作顺序
5.3.3 使用场景
- 生产者-消费者模式 ```cpp // 生产者 data = new_value; std::atomic_thread_fence(std::memory_order_release); ready = true;
// 消费者 while (!ready) { std::this_thread::yield(); } std::atomic_thread_fence(std::memory_order_acquire); value = data;
1
2
3
4
5
6
7
8
9
10
11
2. 双重检查锁定模式
```cpp
if (!initialized) {
std::lock_guard<std::mutex> lock(mutex);
if (!initialized) {
// 初始化操作
std::atomic_thread_fence(std::memory_order_release);
initialized = true;
}
}
5.3.4 注意事项
- 栅栏操作开销较大,应谨慎使用
- 优先使用原子操作的内存序参数,而不是显式栅栏
- 栅栏主要用于需要精确控制内存序的场景
- 过度使用栅栏会影响性能
Contact
Feel free to contact me windmillyucong@163.com anytime for anything.