ALock并发线程的最大个数为一个已知的界限N,同时也要为每个锁分配一个与该界限大小相同的数组。 就算一个线程每次只访问一个锁,同步L个不同对象也需要O(LN)大小的空间。
CLHLock
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
1 public class CLHLock : ILock
2 {
3 private class QNode
4 {
5 public bool Locked { get; set; }
6 }
7
8 private QNode tail;
9
10 [ThreadStatic]
11 private static QNode myPred;
12
13 [ThreadStatic]
14 private static QNode myNode;
15
16 public CLHLock()
17 {
18 tail = new QNode();
19 }
20
21 public void setLock()
22 {
23 myNode = new QNode();
24 myNode.Locked = true;
25 myPred = Interlocked.Exchange<QNode>(ref tail, myNode);
26 while (myPred.Locked) { }
27 }
28
29 public void unlock()
30 {
31 myNode.Locked = false;
32 myNode = myPred;
33 }
34 }
类QNode的布尔型Locked属性记录了每个线程的状态。如果Locked为true,则对应的线程要么已经获得到锁,要么正在等待锁;如果Locked为false,则对应的线程已经释放了锁。线程被顺序地排入“隐式”链表,每个线程通过一个线程局部变量myPred指向前驱线程,公共的tail保存着最近加入到队列的结点。
若要获得锁,线程将自己的myNode的Locked设为true,表示该线程准备获得锁。随后线程对tail调用Interlocked.Exchange方法,把自身的前驱myPred指向队尾,并将自身设置为新队尾。最后线程就在myPred的Locked上旋转,直到myPred释放锁。若要释放锁,线程将其Locked设为false。
CLHLock让每个线程在不同的存储单元上旋转,这样当一个线程释放它的锁时,只能使其后继的cache无效。该算法比ALock所需要的空间少(同步L个不同对象只需要O(L + N)的空间),且不需要知道可能使用锁的线程数量。也提供了先来先服务的公平性。