一:混合锁
混合锁=用户模式锁+内核模式锁
1.用户模式锁:
Thread.Sleep(1);让线程休眠毫秒
Thread.Sleep(0);让线程放弃当前的时间片,让本溪拿出更高或者同等线程得到时间片运行
Thread.YieId():让线程立即放弃当前时间片,让更低级别的线程得到运行,当其他thread时间片用完,本thread再度唤醒。
Yield<Sleep(0)<Sleep(1)
一个时间片等于30ms
2.混合锁有哪些:
SemaphoreSlim
ManualResetEventSlim
ReaderWriterLockSlim
ReaderWriterLockSlimWrapper
相比较内核模式,效率更高
1.ManualResetEventSlim:优化点
构造函数中已经可以不提供默认状态,默认是false,表示合围状态
使用wait代替waitOne(waitHandle提供了一个方法)
支持任务取消
public static ManualResetEventSlim manual = new ManualResetEventSlim(true); static void Main(string[] args) { CancellationTokenSource source = new CancellationTokenSource(); while (true) { Console.WriteLine("-----------"); try { manual.Wait(3, source.Token); Console.WriteLine("Hello Wordl!"); manual.Set(); source.Cancel(); } catch (Exception ex) { Console.WriteLine(ex.Message); break; } } Console.ReadKey(); }
wait()中的实现逻辑
waitone()调用底层的win32函数:
1
2
3[SecurityCritical]
[**MethodImpl**(MethodImplOptions.InternalCall)]
**private** static extern **int** **WaitOneNative**(SafeHandle waitableSafeHandle, **uint** millisecondsTimeout, **bool** hasThreadAffinity, **bool** exitContext);wait():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113// System.Threading.ManualResetEventSlim
[__DynamicallyInvokable]
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
{
this.ThrowIfDisposed();
cancellationToken.ThrowIfCancellationRequested();
if (millisecondsTimeout < -1)
{
throw new ArgumentOutOfRangeException("millisecondsTimeout");
}
if (!this.IsSet)
{
if (millisecondsTimeout == 0)
{
return false;
}
uint startTime = 0u;
bool flag = false;
int num = millisecondsTimeout;
if (millisecondsTimeout != -1)
{
startTime = TimeoutHelper.GetTime();
flag = true;
}
int num2 = 10;
int num3 = 5;
int num4 = 20;
int spinCount = this.SpinCount;
for (int i = 0; i < spinCount; i++)
{
if (this.IsSet)
{
return true;
}
if (i < num2)
{
if (i == num2 / 2)
{
Thread.Yield();
}
else
{
Thread.SpinWait(4 << i);
}
}
else
{
if (i % num4 == 0)
{
Thread.Sleep(1);
}
else
{
if (i % num3 == 0)
{
Thread.Sleep(0);
}
else
{
Thread.Yield();
}
}
}
if (i >= 100 && i % 10 == 0)
{
cancellationToken.ThrowIfCancellationRequested();
}
}
this.EnsureLockObjectCreated();
using (cancellationToken.InternalRegisterWithoutEC(ManualResetEventSlim.s_cancellationTokenCallback, this))
{
object @lock = this.m_lock;
lock (@lock)
{
while (!this.IsSet)
{
cancellationToken.ThrowIfCancellationRequested();
if (flag)
{
num = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout);
if (num <= 0)
{
bool result = false;
return result;
}
}
this.Waiters++;
if (this.IsSet)
{
int waiters = this.Waiters;
this.Waiters = waiters - 1;
bool result = true;
return result;
}
try
{
if (!Monitor.Wait(this.m_lock, num))
{
bool result = false;
return result;
}
}
finally
{
this.Waiters--;
}
}
}
}
return true;
}
return true;
}
2.SemaphoreSlim:信号量
Semaphore的WaitOne或者Release方法的调用大约会耗费1微秒的系统时间,而优化后的SemaphoreSlim则需要大致四分之一微秒。
Semaphore就好像一个栅栏,有一定的容量,当里面的线程数量到达设置的最大值时候,就没有线程可以进去。然后,如果一个线程工作完成以后出来了,那下一个线程就可以进去了。Semaphore的WaitOne或Release等操作分别将自动地递减或者递增信号量的当前计数值。当线程试图对计数值已经为0的信号量执行WaitOne操作时,线程将阻塞直到计数值大于0。在构造Semaphore时,最少需要2个参数。信号量的初始容量和最大的容量。
1 | public static SemaphoreSlim slim = new SemaphoreSlim(2, 10); |
3.ReaderWriterLockSlim
用EnterReadLock代替AcquireReaderLock方法,性能比内核版本要高很多。
1 | public static ReaderWriterLockSlim lockSlim = new ReaderWriterLockSlim(); |
混合锁:先在用户模式下内旋,如果超过一定的值,会切换到内核锁。
在内旋的情况下,我们会看到大量的Sleep(0),Sleep(1),Yield等语法