《多处理器编程的艺术》读书笔记(6)--- 队列锁

      在BackoffLock算法中有两个问题:1.cache一致性流量:所有线程都在同一个共享存储单元上旋转,每一次成功的锁访问都会产生cache一致性流量(尽管比TASLock低);2.临界区利用率低:线程延迟过长,导致临界区利用率低下。

      可以将线程组织成一个队列来克服这些缺点。在队列中,每个线程检测其前驱线程是否已完成来判断是否轮到自己。让每个线程在不同的存储单元上旋转,从而降低cache一致性流量。队列还提高了临界区的利用率,因为没有必要去判断何时要访问它:每个线程直接由队列中的前驱线程来通知。最后,队列提供先来先服务的公平性。

基于数组的锁 ALock

ContractedBlock.gif ExpandedBlockStart.gif Code
 1 public class ALock : ILock
 2 {
 3     [ThreadStatic]
 4     private static int mySlotIndex;
 5 
 6     private int tail;
 7     private bool[] flag;
 8     private int size;
 9 
10     public ALock(int capacity)
11     {
12         size = capacity;
13         tail = -1;
14         flag = new bool[capacity];
15         flag[0= true;
16     }
17 
18     public void setLock()
19     {
20         int slot = Interlocked.Increment(ref tail) % size;
21         mySlotIndex = slot;
22         while (!flag[slot]) { }
23     }
24 
25     public void unlock()
26     {
27         flag[mySlotIndex] = false;
28         flag[(mySlotIndex + 1% size] = true;
29     }
30 }

      tail被所有的线程共享,其初始值为-1。为了获得锁,每个线程原子地增加tail。所得的结果称为线程的槽。槽则被当作布尔数据flag的索引。如果flag[j]为true,那么槽为j的线程有权获得锁。在初始状态时,flag[0]为true。为了获得锁,线程不断地旋转直到它的槽所对应的flag变为true。而在释放锁时,线程把对应于它自己槽的flag设为false,并将下一个槽的flag设为true。数组的大小size就是最大的并发线程数。

      在ALock算法中,mySlotIndex是线程的局部变量。线程的局部变量与线程的常规变量不同,对于每个局部变量,线程都有它自己独立初始化的副本。局部变量不需要保存在共享存储器中,不需要同步,也不会产生任何一致性流量。

      数组flag[]是被多个线程所共享。但在任意给定的时间,由于每个线程都是在一个数组存储单元的本地cache副本上旋转,大大降低了无效流量,从而使得对数组存储单元的争用达到最小。但要注意争用仍有可能发生,当相邻的数据项(如数组元素)共享同一cache线时,对一个数据项的写将会使该数据项的cache线无效,对于那些恰好进入同一个cache线的未改变但很接近的数据项这种写将会引起正在这些数据上进行旋转的处理器的无效流量。

转载于:https://www.cnblogs.com/pennant/archive/2009/10/22/1588344.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值