Spin Lock -- Anderson’s Algorithm

Anderson’s Algorithm基于队列实现,也就是基于数组的算法。


算法实现:

1)定义共享数组变量flag,大小为N,也就是线程的个数。初始化数组为{true,false,false,......,false}

2)定义共享变量tail,用于记录当前获取锁的线程。

3)每个线程都有一个记录当前槽的临时变量slot。

4)lock: 获取tail变量存储到临时变量slot中,然后将tail++。利用while进行自旋,自旋的条件是slot % n对应的槽为false。

5)unlock:更新slot % n对应的槽为false,更新 (slot + 1)% n的槽为true。

假设某个线程A获取锁,获取锁之前tail = i,获取锁之后tail + 1,假设之后另外一个线程B请求锁,这时tail = i + 1,由于(i + 1) % n处为false,因此这个线程自旋。

此时,获取A释放锁,然后设置了 (i + 1) % n处为true,则线程B退出自旋状态,获取锁。依照这样的方式,之后的第i + 1,i + 2,.....线程依次获取锁。

虽然这个算法并没有实际的队列结构,但是通过数组以及标志位的移动模拟了队列,这样就实现了基于队列的自旋锁。

这样做的好处是不会产生饥渴的现象,先入队列的线程会先获取锁。


实现上面的功能需要注意的是:

1)在lock中定义的局部变量,如何在unlock中继续使用。因为实现的锁和线程不应该有很高的内聚,所以建议使用一个hashmap保存这些变量,key值采用线程的id。

map.put(Thread.currentThread().getId(), slot);

2)flag数组,如果采用正常的boolean或者int,会占用较大的内存。建议采用bit数组以及位操作进行模拟。


来看看这个算法和TTAS的性能对比,


来看看其缺点是什么,


上面图的意识是,thread0~thread3需要获取的flag[0]~flag[3]在同一缓存行内,当thread2修改flag[2]为false的时候,系统会标记其处于的缓存行(cache line)失效,也就是说当其他几个线程想获取其对应的flag的时候会发现缓存行失效,强制从内存中读取新的值,即使这个值并没有被更改。我们知道如果L1,L2,L3级缓存和内存的读取效率依次降低,这就影响了性能。

解决方法是,进行填充恰好使得每个线程需要的flag不在同一个缓存行内(cache line)。


另外一个缺点就是,比较消耗空间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值