[转]memory order,memory barrier,原子操作

转自

https://www.cnblogs.com/the-tops/p/6347584.html

编译时memory moder:https://preshing.com/20120625/memory-ordering-at-compile-time/

强弱memory order:https://preshing.com/20120930/weak-vs-strong-memory-models/

https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/

acquire/release:  https://preshing.com/20120913/acquire-and-release-semantics/

 看这篇文章:https://www.cl.cam.ac.uk/~pes20/ppc-supplemental/test7.pdf

看这篇文章 : https://preshing.com/20140709/the-purpose-of-memory_order_consume-in-cpp11/

                   https://preshing.com/20130823/the-synchronizes-with-relation/

                  https://preshing.com/20130618/atomic-vs-non-atomic-operations/

                   https://github.com/preshing/ConsumeDemo

编译时的:1. volatile关键字: volatile int  counter;

                  2. __asm__ volatile("" : : : "memory");

 

关于memory_order这个概念,非常的令人困惑。其关键就是atomic能够保证单个的操作的原子性,但不能保证两个原子操作之间的顺序,这涉及到CPU对缓存刷新时进行的顺序重排。这里看两个简单的例子就可以理解[转]memory order,memory barrier,原子操作

 [转]memory order,memory barrier,原子操作

 

 

和我们平时的理解完全不一样,内存的修改顺序和实际的顺序居然可能不一致,这就是为什么会引入memory_order这个概念了。

作者:码农苍耳
链接:https://www.jianshu.com/p/83f75ce281a2

 

 

2.1 Sequentially Consistent

该模型是最强的同步模式,参数表示为std::memory_order_seq_cst,同时也是默认的模型。

-Thread 1- -Thread2-
y = 1if(x.load() ==2)
x.store (2); assert (y ==1)

对于上面的例子,即使x和y是不相关的,通常情况下处理器或者编译器可能会对其访问进行重排,但是在seq_cst模式下,x.store(2)之前的所有memory accesses都会happens-before在这次store操作。

另外一个角度来说:对于seq_cst模式下的操作,所有memory accesses操作的重排不允许跨域这个操作,同时这个限制是双向的。

2.2 Acquire/Release

查看下面的典型Acquire/Release的使用例子:

std::atomic<int> a{0};
intb =0;

-Thread 1-
b = 1;
a.store(1, memory_order_release);

-Thread 2-
while(a.load(memory_order_acquire) !=1)/*waiting*/;
std::cout<< b <<'n';

毫无疑问,如果是seq_cst,那么上面的操作一定是成功的(打印变量b显示为1)。

a. memory_order_release保证在这个操作之前的memory accesses不会重排到这个操作之后去,但是这个操作之后的memory accesses可能会重排到这个操作之前去。通常这个主要是用于之前准备某些资源后,通过store+memory_order_release的方式”Release”给别的线程;

b. memory_order_acquire保证在这个操作之后的memory accesses不会重排到这个操作之前去,但是这个操作之前的memory accesses可能会重排到这个操作之后去。通常通过load+memory_order_acquire判断或者等待某个资源,一旦满足某个条件后就可以安全的“Acquire”消费这些资源了

2.3 Consume

这是一个相比Acquire/Release更加宽松的内存模型,对非依赖的变量也去除了happens-before的限制,减少了所需同步的数据量,可以加快执行的速度。

-Thread 1-
n = 1
m = 1
p.store (&n, memory_order_release)

-Thread 2-
t = p.load (memory_order_acquire);
assert( *t == 1&& m ==1);

-Thread 3-
t = p.load (memory_order_consume);
assert( *t == 1&& m ==1);

线程2的assert会pass,而线程3的assert可能会fail,因为n出现在了store表达式中,算是一个依赖变量,会确保对该变量的memory access会happends-before在这个store之前,但是m没有依赖关系,所以不会同步该变量,对其值不作保证。

Comsume模式因为降低了需要在硬件之间同步的数量,所以理论上其执行的速度会比之上面的内存模型块一些,尤其在共享内存大规模数据量情况下,应该会有较明显的差异表现出来。

在这里,Acquire/Consume~Release这种线程间同步协作的机制就被完全暴露了,通常会形成Acquired/Consume来等待Release的某个状态更新。需要注意的是这样的通信需要两个线程间成对的使用才有意义,同时对于没有使用这个内存模型的第三方线程没有任何作用效果。

2.4 Relaxed

最宽松的模式,memory_order_relaxed没有happens-before的约束,编译器和处理器可以对memory access做任何的re-order,因此另外的线程不能对其做任何的假设,这种模式下能做的唯一保证,就是一旦线程读到了变量var的最新值,那么这个线程将再也见不到var修改之前的值了。

这种情况通常是在需要原子变量,但是不在线程间同步共享数据的时候会用,同时当relaxed存一个数据的时候,另外的线程将需要一个时间才能relaxed读到该值,在非缓存一致性的构架上需要刷新缓存。在开发的时候,如果你的上下文没有共享的变量需要在线程间同步,选用Relaxed就可以了。

原文链接: https://www.cnblogs.com/yi-mu-xi/p/11597753.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    [转]memory order,memory barrier,原子操作

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/403627

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月23日 上午9:17
下一篇 2023年4月23日 上午9:17

相关推荐