原子性内存栅栏问题简述

内存栅栏(Memory fence),也称为内存屏障(Memory barrier),以前碰到这类概念都是基于锁的理解,比如dispatch_barrier。在看bmalloc的源代码时,发现其在使用原子性(atomic)的时候,出现了一种memory_order的概念,所以这里来简单看看原子性方面的问题。

Atomic方法

在iOS中,我们平时最常用的原子特性大部分来自于OSAtomic,可是当我打开这个文件的时候,却发现这些方法已经被弃用了。

OSATOMIC_DEPRECATED_REPLACE_WITH(atomic_compare_exchange_strong)
__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0)
bool OSAtomicCompareAndSwap32( int32_t __oldValue, int32_t __newValue, volatile int32_t *__theValue );

很好奇这个新的方法,它来自于C标注库stdatomic.h中,而这个头文件是C11才引入的,说明这是一个非常新的方法。而在C++11中,STL增强了C++的多线程编程能力,所以也是这个时候引入的std::atomic

atomicvolidateatomic是真正的原子性,而volidate则仅标记对象为易变,提交给编译器优化使用的,并不能确保原子性。

Memory Order

typedef enum memory_order {
  memory_order_relaxed = __ATOMIC_RELAXED,
  memory_order_consume = __ATOMIC_CONSUME,
  memory_order_acquire = __ATOMIC_ACQUIRE,
  memory_order_release = __ATOMIC_RELEASE,
  memory_order_acq_rel = __ATOMIC_ACQ_REL,
  memory_order_seq_cst = __ATOMIC_SEQ_CST
} memory_order;

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

fence1.png
fence2.png

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

Spin lock

最后再来看看SpinLock的实现,所有SpinLock都是基于原子操作进行的,目前我碰到的大致分为两种:

  1. 比较无赖,强制for循环等待
  2. 比较友善,在超过一定循环次数,会放弃当前时间片

伪代码:

atomic flag
while flag:
    loop_count++
    if loop_count > MAX_LOOP_COUNT:
        yield  // iOS中可以是thread_swtich

所以当某些调度算法,即使是在优先级高的线程中yield放弃时间片,依然不能分配给优先级低的线程,就会导致优先级反转而死锁。

参考

A Tutorial Introduction to the ARM and POWER Relaxed Memory Models

你可能感兴趣的:(原子性内存栅栏问题简述)