1 /// <summary>
2 /// 自旋、线程所有权、递归
3 /// 构成混合锁
4 /// </summary>
5 internal sealed class AnotherHybridLock : IDisposable
6 {
7 //基元用户模式构造(Interlocked的方法)使用
8 private int m_waiters = 0;
9
10 //基元内核模式构造
11 //AutoResetEvent通知正在等待的线程已发生事件
12 // true将初始状态设置为终止状态; false将初始状态设置为非终止
13 //true有信号(不会阻塞),false无信号
14 private AutoResetEvent m_waiterLock = new AutoResetEvent(false);
15
16 //控制自旋,希望能提升性能
17 //选择一个计数
18 private int m_spinCount = 4000;
19
20 //指出哪个线程拥有锁
21 private int m_owningThreadId = 0;
22
23 //指出拥有锁的线程拥有了多少次锁
24 private int m_recursion = 0;
25
26 public void Enter()
27 {
28 //获取当前托管线程的唯一标识符
29 int threadId = Thread.CurrentThread.ManagedThreadId;
30
31 //如果调用线程已经拥有锁
32 if (threadId == m_owningThreadId)
33 {
34 //递增递归计数并返回
35 m_recursion++;
36 return;
37 }
38
39 //调用线程未拥有锁,尝试获取它
40 //为基于自旋的等待提供支持
41 SpinWait spinWait = new SpinWait();
42 for (int spinCount = 0; spinCount < m_spinCount; spinCount++)
43 {
44 //第一个线程时m_waiters始终返回0,直接获得锁
45 if (Interlocked.CompareExchange(ref m_waiters, 1, 0) == 0)
46 {
47 goto GotLock;
48 }
49
50 //给其它线程运行的机会,希望锁会被释放
51 spinWait.SpinOnce();
52 }
53
54 //自旋结束,锁仍未获得,再试一次
55 //m_waiters若等于0,则1>1不成立,不必WaitOne,直接获得锁
56 //否则WaitOne
57 if (Interlocked.Increment(ref m_waiters) > 1)
58 {
59 //仍然是竞争条件,这个线程必须被阻塞
60 //等待锁,性能有较大损失
61 m_waiterLock.WaitOne();
62 //等这个线程醒来时,它拥有锁,设置一些状态并返回
63 }
64
65 GotLock:
66 //线程获得锁时,记录线程Id
67 m_owningThreadId = threadId;
68 //指出该线程拥有锁1次
69 m_recursion = 1;
70 }
71
72 public void Leave()
73 {
74 //如果调用线程不拥有锁,则表明存在bug
75 int threadId = Thread.CurrentThread.ManagedThreadId;
76 if (threadId != m_owningThreadId)
77 {
78 //当某个方法要求调用方拥有给定 Monitor 上的锁并且该方法由不拥有该锁的调用方调用时引发的异常
79 throw new SynchronizationLockException("Lock not owned by calling thread");
80 }
81
82 //递减递归计数
83 --m_recursion;
84 //如果该线程仍然拥有锁
85 if (m_recursion > 0)
86 {
87 //直接返回
88 return;
89 }
90
91 //执行到这里说明现在没有线程拥有锁
92 m_owningThreadId = 0;
93
94 //如果没有其它线程在等待则直接返回
95 if (Interlocked.Decrement(ref m_waiters) == 0)
96 {
97 return;
98 }
99
100 //否则,有其它线程正在等待,唤醒其中一个
101 //这里会有较大的性能损失
102 m_waiterLock.Set();
103 }
104
105 public void Dispose()
106 {
107 m_waiterLock.Dispose();
108 }
109 }