原子指令于Lock-Free数据结构教学笔记

原文:http://faculty.ycp.edu/~dhovemey/spring2011/cs365/lecture/lecture20.html

示例代码:AtomicInstructions.zip

原子指令

原子指令是特殊的硬件指令,以不可分的方式对一个或多个内存位置执行操作。无论其他处理器执行什么指令,原子操作都会成功或完全失败。

原子指令可以用来做同步处理。由于原子指令可用于更改共享数据而无需获取和释放锁,因此可以实现更高的并行性。但是,由于它们是低层的,并且只能对数据结构进行小的更新,因此使用它们来实现并行数据结构是一项艰巨的任务。

原子指令的例子:

  • 原子增量 atomic increment

  • 原子交换寄存器和内存位置 atomic exchange register and
    memory location

  • 比较与交换 compare and swap

比较与交换

比较与交换(CAS)通常用作无锁数据结构的基元。它的行为由以下C函数描述,其中“ATOMIC {…}表示以原子方式执行的代码块:

int compareAndSwap(int * loc,int expectedValue,int valueToStore){
        ATOMIC {
                int val = * loc;
                if(val == expectedValue){
                        * loc = valueToStore;
                }
                返回;
        }
}

(上面的函数假定内存位置的内容是一个整数,但它可以是任何原始数据类型,包括指针类型。)

Java中的原子指令

java.util.concurrent.atomic中封装 表示内存位置的数据类型,其内存位置可以通过原子机器指令访问(如果主机CPU具有所需的硬件指令)。
这些数据类型类似于内置的“wrapper”数据类型,例如java.lang.Integer,它们用于定义封装原始值的对象。

AtomicInteger是具有原子操作的共享整数,AtomicReference是具有原子操作的共享引用(指针)等。

应用

共享计数器
原子增量操作可用于实现共享计数器。
伪随机数生成器
线性同余伪随机数生成器 (如java.util.Random)的工作原理是使用递推以生成基于初始种子值的一系列整数的值。每次调用者想要获得序列的下一个值时,生成器必须

  1. 获取当前种子值
  2. 使用递推方程生成下一个种子
  3. 存储更新的种子值
  4. 返回下一个种子值派生的值

步骤1-3必须以原子方式执行,因为当不同的线程同时请求序列的下一个成员时,它们不能返回相同的成员。可以使用互斥锁确保原子性,但如果许多线程同时使用生成器,则产生的竞争可能会降低性能(因为线程争用互斥锁。)步骤1-3必须以原子方式执行,因为当不同的线程同时请求序列的下一个成员时,它们不能返回相同的成员。可以使用互斥锁确保原子性,但如果许多线程同时使用生成器,则产生的竞争可能会降低性能(因为线程争用互斥锁。)
CAS操作可用于实现乐观并发。完成上述算法中的步骤1-2而不获取锁。仅当没有其他线程更改它时,步骤3才使用CAS操作来存储更新的下一个种子值 。如果CAS成功,则执行步骤4并完成操作。如果CAS失败,因为另一个线程更新了种子,则操作返回到步骤1并再次尝试。
队列数据结构
这是一篇描述使用CAS操作而不是互斥锁实现的队列数据结构的文章:Michael,Maged和Scott,Michael。 简单,快速,实用的非阻塞和阻塞并发队列算法,PODC 1996。
这些算法也在Michael Scott的网站上作为伪代码:
http://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html
本文还描述了一种基于锁的队列算法,其中使用单独的锁来保护队列的头部和尾部,允许并行和出列操作并行进行。

猜你喜欢

转载自blog.csdn.net/weixin_40165747/article/details/83028112