https://sf-zhou.github.io/brpc/brpc_01_bthread.html
不保证同步操作,不会将一定的顺序强加到并发内存访问上,只保证原子性和修改顺序一致性
x = y = 0 // Thread 1: r1 = y.load(std::memory_order_relaxed); // A x.store(r1, std::memory_order_relaxed); // B // Thread 2: r2 = x.load(std::memory_order_relaxed); // C y.store(42, std::memory_order_relaxed); // D
D 能出现在 A前面,B 能出现在 C前面,D也能出现在C前面,因为编译器重排和运行期乱序,最后的结果是可能是 r1 == r2 == 42 的
松弛内存序通常用于计数器的递增,例如对 shared_ptr 引用计数的递增,但是递减不是。
总结:存在一个全序修改序,在同一线程对同一个内存地址的操作是不会重排的。
x.compare_exchange_weak( oldVal, // expected oldVal+1, // desired memory_order_relaxed);
memory_order_release 的 store 的线程 A,和 memory_order_acquire
的 load 的线程 B。
所有的写内存的操作(非原子的,松弛内存写)都将会会 happened-before 于 store 线程 A,在 A 看来,
所有的写操作都完成了,对于线程B来说,所有的写都将变得可见,也就是说,一旦B完成了 load 操作,线程B能看到所有
线程A写到内存的东西。这个承诺仅在一个 load 线程B返回了 A 所 store 的值,或者在接下来的 release 序列中。
一个线程内任何 happened-before 于 store 的事件,都将在 load 的线程中作为副作用(可见),换句话说,load 的副作用让 store 前的事件变得可见。
同步建立在相同的变量上进行 release 和 acquire,其他未同步的线程将会见到不同的内存序
在强序的系统上,比如在X86,IBM上,release-acquire 是大多数的指令操作是自动会应用的,不需要提供其他的同步指令,在一些弱序架构上,比如ARM和Itanium上就需要在load前加上memory fence,还有会影响一些编译器的优化。
这种序通常会用在mutex和spin lock上,也就是说当修改线程在临界区执行完成后,任何修改对于其他线程都是可见的。
总结:所有在 store-release 所在线程的操作对于 load-acquire 都是可见的。
(相关的细节还在修订,并且暂时不鼓励)
和 memory_order_release-memory_order_acquire 的区别在于,只会在依赖的变量上是可见的。当 memory_order_consume 的 load 完成后,标记 memory_order_release 在原子变量的修改对于所有在 load 之后且在这个变量上的函数和操作都是可见的。
DEC Alpha 的主流 cpu 都是自动支持这种序的。
是 release-acquire 的轻量版本,带入了依赖,consume 依赖某个变量 X,那么在 store-release 的线程中对变量X的操作都是先发于(happend-before) consume
原子操作默认是seq_consist
bool load(memory_order __m = memory_order_seq_cst) const noexcept { return _M_base.load(__m); }
memory_order_seq_cst 不仅仅用 memory_order_release-memory_order_acquire 的顺序排序,还将对带有 memory_order_seq_cst
的原子操作提供一个单独的全序修改顺序 single total modification order 。
操作B memory_order_seq_cst
的 load 原子变量 M,遵从下面这些条件:
memory_order_seq_cst
,则B可能观察到不是single total order 全序偏序:
偏序指的是只能为系统中的部分事件定义先后顺序。这里的部分其实是有因果关系的事件。
全序是指所有的事件都可以区分先后顺序。无论是真实或是虚拟世界,这都是受欢迎的。因为,所有的事件都有了一个统一的评判标准,我们一直欢迎统一而拒绝分裂。
lamport http://zhangtielei.com/posts/blog-time-clock-ordering.html
Sequential ordering 可能对于多个 producer 和多个 consumer 有效,并且观测到所有 producer 的都保持一个一致的顺序。Total sequential ordering 会要求一个完全的内存栅栏指令到多核系统中,会成为系统的瓶颈。
其他:
https://www.think-cell.com/en/career/talks/pdf/think-cell_talk_memorymodel.pdf
正确的线程间同步
实现lock-free算法
![[Pasted image 20220422175119.png]]
cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size
查看Cache Line 的大小,会顺序加载64字节
如何知道在内存还是在cache内?内存和cache如何对应
一种是 Direct Mapped Cache 来做,通过对内存取模映射到 cacheline上
多个内存缓存在一起怎么办?cache line中有组标记 tag,区分不同的内存块,和有效位 valid,判断是否需要强制从内存取值。内存地址被分成 组标记 + 索引 + 偏移量 得到在 cache block 的地址。
缓存命中率提高:
unlikely
sched_setaffinity
什么时候才把cache的数据写回内存?
https://zhuanlan.zhihu.com/p/269221065
Memory Models for C/C++ Program:
https://arxiv.org/pdf/1803.04432.pdf