常用技术

原子操作
Mutex 类
SemaphoreSlim 类
AutoResetEvent 类
ManualResetEventSlim 类
CountDownEvent 类
Barrier 类
ReaderWriterLockSlim 类
SpinWait 类

相关概念

多线程

线程是程序中一个单一的顺序控制流程.在单个程序中同时运行多个线程完成不同的工作,称为多线程。如果某个线程进行一次长延迟操作, 处理器就切换到另一个线程执行。这样,多个线程的并行(并发)执行隐藏了长延迟,提高了处理器资源利用率,从而提高了整体性能。
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率

多线程的优势

进程有独立的地址空间,同一进程内的线程共享进程的地址空间。启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。

进程

进程,是操作系统进行资源调度和分配的基本单位。是由进程控制块、程序段、数据段三部分组成。一个进程可以包含若干线程(Thread),线程可以帮助应用程序同时做几件事。
在程序被运行后中,系统首先要做的就是为该程序进程建立一个默认线程,然后程序可 以根据需要自行添加或删除相关的线程。它是可并发执行的程序。在一个数据集合上的运行过程,是系统进行资源分配和调度的一个独立单位,也是称活动、路径或任务,它有两方面性质:活动性、并发性。
进程可以划分为运行、阻塞、就绪三种状态,并随一定条件而相互转化:就绪--运行,运行--阻塞,阻塞--就绪。

线程(thread)

线程是CPU调度和执行的最小单位。有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。
线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。

主线程

进程创建时,默认创建一个线程,这个线程就是主线程。主线程是产生其他子线程的线程,同时,主线程必须是最后一个结束执行的线程,它完成各种关闭其他子线程的操作。尽管主线程是程序开始时自动创建的,它也可以通过Thead类对象来控制,通过调用CurrentThread方法获得当前线程的引用

原子操作

在多线程环境中,不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch(上下文切换) 
System.Threading.Interlocked 静态类

context switch(上下文切换)

在多任务处理系统中,CPU需要处理所有程序的操作,当用户来回切换它们时,需要记录这些程序执行到哪里。上下文切换就是这样一个过程,它允许CPU记录并恢复各种正在运行程序的状态,使它能够完成切换操作。
上下文切换过程中的“页码”信息是保存在进程控制块(PCB)中的。即 “切换桢”(switchframe)。
在三种情况下可能会发生上下文切换:中断处理,多任务处理,用户态切换。

内核模式

CPU将线程置于阻塞状态,线程挂起时间比较长

用户模式

线程等待时间较短,不用将线程切换到阻塞状态

混合模式

先尝试用户模式,如果等待时间较长,则将线程切换到阻塞状态,以节省CPU资源

线程同步有:临界区、互斥区、事件、信号量四种方式
临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event)的区别
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。
2、互斥量:采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享
3、信号量:它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目
4、事 件: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作

Interlocked 类

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
public class CounterModule
{
public static void TestCounter(CounterBase c)
{
for (var i=0; i<100000; i++) {
c.Increment();
c.Decrement();
}
}

public abstract class CounterBase
{
public abstract void Increment();

public abstract void Decrement();
}

public class Counter : CounterBase
{
private int _count;

public int Count
{
get
{
return _count;
}
}
public override void Decrement()
{
_count--;
}

public override void Increment()
{
_count++;
}
}
public class CounterNoLock : CounterBase
{
private int _count;

public int Count
{
get
{
return _count;
}
}
public override void Decrement()
{
Interlocked.Decrement(ref _count);
}

public override void Increment()
{
Interlocked.Increment(ref _count);
}
}
}

public static void TestInterlocked() {
Console.WriteLine("Incorrect counter");
Counter c = new Counter();

Thread t1 = new Thread(() => TestCounter(c));
Thread t2 = new Thread(() => TestCounter(c));
Thread t3 = new Thread(() => TestCounter(c));

t1.Start();
t2.Start();
t3.Start();

t1.Join();
t2.Join();
t3.Join();

Console.WriteLine("TotalCount:{0}", c.Count);
Console.WriteLine("-----------------------");

Console.WriteLine("correct counter");
CounterNoLock d = new CounterNoLock();

t1 = new Thread(() => TestCounter(c));
t2 = new Thread(() => TestCounter(c));
t3 = new Thread(() => TestCounter(c));

t1.Start();
t2.Start();
t3.Start();

t1.Join();
t2.Join();
t3.Join();

Console.WriteLine("TotalCount:{0}", c.Count);
}
Interlocked 提供了 Increment、Decrement 和 Add 等基于数据操作的原子方法
  • 共享打印机
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
class PrinterWithInterlockTest
{
/// <summary>
/// 正在使用的打印机
/// 0代表未使用,1代表正在使用
/// </summary>
public static int UsingPrinter = 0;
/// <summary>
/// 计算机数量
/// </summary>
public static readonly int ComputerCount = 3;
/// <summary>
/// 测试
/// </summary>
public static void TestPrint()
{
Thread thread;
Random random = new Random();
for (int i = 0; i < ComputerCount; i++)
{
thread = new Thread(MyThreadProc);
thread.Name = string.Format("Thread{0}",i);
Thread.Sleep(random.Next(3));
thread.Start();
}
}
/// <summary>
/// 线程执行操作
/// </summary>
private static void MyThreadProc()
{
//使用打印机进行打印
UsePrinter();
//当前线程等待1秒
Thread.Sleep(1000);
}
/// <summary>
/// 使用打印机进行打印
/// </summary>
private static bool UsePrinter()
{
//检查大引进是否在使用,如果原始值为0,则为未使用,可以进行打印,否则不能打印,继续等待
if (0 == Interlocked.Exchange(ref UsingPrinter, 1))
{
Console.WriteLine("{0} acquired the lock", Thread.CurrentThread.Name);

//Code to access a resource that is not thread safe would go here.

//Simulate some work
Thread.Sleep(500);

Console.WriteLine("{0} exiting lock", Thread.CurrentThread.Name);

//释放打印机
Interlocked.Exchange(ref UsingPrinter, 0);
return true;
}
else
{
Console.WriteLine(" {0} was denied the lock", Thread.CurrentThread.Name);
return false;
}
}

}
  • 共享打印机,使用 lock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <summary>
/// 正在使用的打印机
/// </summary>
private static object UsingPrinterLocker = new object();
/// <summary>
/// 使用打印机进行打印
/// </summary>
private static void UsePrinter()
{
//临界区
lock (UsingPrinterLocker)
{
Console.WriteLine("{0} acquired the lock", Thread.CurrentThread.Name);
//模拟打印操作
Thread.Sleep(500);
Console.WriteLine("{0} exiting lock", Thread.CurrentThread.Name);
}
}
  • 共享打印机,使用 Monitor “监视器”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <summary>
/// 使用打印机进行打印
/// </summary>
private static void UsePrinterWithMonitor()
{
System.Threading.Monitor.Enter(UsingPrinterLocker);
try
{
Console.WriteLine("{0} acquired the lock", Thread.CurrentThread.Name);
//模拟打印操作
Thread.Sleep(500);
Console.WriteLine("{0} exiting lock", Thread.CurrentThread.Name);
}
finally
{
System.Threading.Monitor.Exit(UsingPrinterLocker);
}
}
lock 关键字就是用 Monitor 类来实现的,使用 lock 关键字通常比直接使用 Monitor 类更可取,一方面是因为 lock 更简洁,另一方面是因为 lock 确保了即使受保护的代码引发异常,也可以释放基础监视器。这是通过 finally 关键字来实现的,无论是否引发异常它都执行关联的代码块。

Mutex 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void TestMutex() {
const string MutexName = "CSharpThreadingCookBook";
//定义互斥量
using (var m = new Mutex(false, MutexName)){
//获取互斥量
if (!m.WaitOne(TimeSpan.FromSeconds(5), false))
{
Console.WriteLine("Second instance is running!");
}
else {
Console.WriteLine("Running!");

//释放Mutex
m.ReleaseMutex();
}
}
}
Mutex 类,只对一个线程授予对共享资源的独占访问
具名的互斥量是全局的操作系统对象,务必正确关闭互斥量。最好使用 using 代码块包裹互斥量对象
跨进程,win32封装的,所以它所需要的互操作转换更耗资源。

SemaphoreSlim 类

信号量,可用于在一个预计等待时间会非常短的进程内进行等待。 SemaphoreSlim 会尽可能多地依赖由公共语言运行时 (CLR) 提供的同步基元。 但是,它也会根据需要提供延迟初始化的、基于内核的等待句柄,以支持等待多个信号量。 SemaphoreSlim 还支持使用取消标记,但它不支持命名信号量或使用等待句柄来进行同步。

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
public static void TestSemaphoreSlim()
{
for (var i = 0; i < 10; i++)
{
string threadName = "Thread" + i;
int secondsToWait = 2 * (i + 1);
//运行 10 个,不同名称和初始运行时间的线程
Thread t = new Thread(() => AccessDataBase(threadName, secondsToWait));
t.Start();
}
}

//创建实例,限制并发线程数量为 4
private static SemaphoreSlim m_semaphore = new SemaphoreSlim(4);

private static void AccessDataBase(string name, int seconds)
{
Console.WriteLine("{0} 等待访问数据库!", name);
m_semaphore.Wait();

Console.WriteLine("{0} 获得访问数据库的权限!", name);
Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine("{0} 访问数据库完成!", name);
m_semaphore.Release();
}
SemaphoreSlim 类,限制并发线程数量,并不适用 Windows 内核信号量,也不支持进程间同步。因此在跨程序同步的场景下可以使用 Semaphore 类。
  • 支付流程
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
class PaymentWithSemaphore
{
/// <summary>
/// 声明收银员总数为3个,但是当前空闲的个数为0,可能还没开始上班。
/// </summary>
private static Semaphore IdleCashiers = new Semaphore(0, 3);
/// <summary>
/// 测试支付过程
/// </summary>
public static void TestPay()
{
ParameterizedThreadStart start = new ParameterizedThreadStart(Pay);
//假设同时有5个人来买票
for (int i = 0; i < 5; i++)
{
Thread thread = new Thread(start);
thread.Start(i);
}

//主线程等待,让所有的的线程都激活
Thread.Sleep(1000);
//释放信号量,2个收银员开始上班了或者有两个空闲出来了
IdleCashiers.Release(2);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
public static void Pay(object obj)
{
Console.WriteLine("Thread {0} begins and waits for the semaphore.", obj);
IdleCashiers.WaitOne();
Console.WriteLine("Thread {0} starts to Pay.",obj);
//结算
Thread.Sleep(2000);
Console.WriteLine("Thread {0}: The payment has been finished.",obj);

Console.WriteLine("Thread {0}: Release the semaphore.", obj);
IdleCashiers.Release();
}
}

同步事件有两种:AutoResetEvent 和 ManualResetEvent。它们之间唯一的不同在于,无论何时,只要 AutoResetEvent 激活线程,它的状态将自动从终止变为非终止。相反,ManualResetEvent 允许它的终止状态激活任意多个线程,只有当它的 Reset 方法被调用时才还原到非终止状态。

AutoResetEvent 类

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
public static void TestAutoResetEvent() {
Thread t = new Thread(() => AutoResetProcess(10));
t.Start();

Console.WriteLine("等待另一个线程完成工作 ......");
m_workerEvent.WaitOne();
Console.WriteLine("第一个工作完成!");

Console.WriteLine("工作运行在主线程 ......");
Thread.Sleep(TimeSpan.FromSeconds(5));
m_mainEvent.Set();

Console.WriteLine("开启第二个线程运行第二个工作 ......");
m_workerEvent.WaitOne();
Console.WriteLine("第二个工作完成!");

}

private static AutoResetEvent m_workerEvent = new AutoResetEvent(false);
private static AutoResetEvent m_mainEvent = new AutoResetEvent(false);

private static void AutoResetProcess(int seconds) {
Console.WriteLine("开启一个延时工作 ......");

Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine("工作完成!");
m_workerEvent.Set();

Console.WriteLine("等待主线程完成工作!");
m_mainEvent.WaitOne();

Console.WriteLine("开启第二个工作 ......");

Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine("工作完成!");
m_workerEvent.Set();

}
AutoResetEvent 类,从一个线程向另一个线程发送通知,可以通知等待的线程有某事件发生。
AutoResetEvent 构造函数中传入 false,定义了两个实例的初始状态为 unsignaled 意味着任何线程调用这两个对象中的任何一个 WaitOne 方法将会被阻塞,直到我们调用了 Set 方法;
如果构造函数中传入 true,定义了两个实例的初始状态为 signaled 线程调用 WaitOne 方法则会被立即执行,然后事件状态自动变成 unsignaled,需要对该实例调用 Set 方法,以便其他线程对该实例调用 WaitOne 方法从而继续执行。
类似旋转门,一次只能通过一个
  • 熬粥、蒸鱼
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
class CookResetEvent
{
/// <summary>
///
/// </summary>
private AutoResetEvent resetEvent = new AutoResetEvent(false);
/// <summary>
/// 做饭
/// </summary>
public void Cook()
{
Thread porridgeThread = new Thread(new ThreadStart(Porridge));
porridgeThread.Name = "Porridge";
porridgeThread.Start();

Thread makeFishThread = new Thread(new ThreadStart(MakeFish));
makeFishThread.Name = "MakeFish";
makeFishThread.Start();

//等待5秒
Thread.Sleep(5000);

resetEvent.Reset();
}
/// <summary>
/// 熬粥
/// </summary>
public void Porridge()
{
//选材
Console.WriteLine("Thread:{0},开始选材", Thread.CurrentThread.Name);

//淘米
Console.WriteLine("Thread:{0},开始淘米", Thread.CurrentThread.Name);

//熬制
Console.WriteLine("Thread:{0},开始熬制,需要2秒钟", Thread.CurrentThread.Name);
//需要2秒钟
Thread.Sleep(2000);
Console.WriteLine("Thread:{0},粥已经做好,锅闲了", Thread.CurrentThread.Name);

resetEvent.Set();
}
/// <summary>
/// 做鱼
/// </summary>
public void MakeFish()
{
//洗鱼
Console.WriteLine("Thread:{0},开始洗鱼",Thread.CurrentThread.Name);

//腌制
Console.WriteLine("Thread:{0},开始腌制", Thread.CurrentThread.Name);

//等待锅空闲出来
resetEvent.WaitOne();

//烹调
Console.WriteLine("Thread:{0},终于有锅了", Thread.CurrentThread.Name);
Console.WriteLine("Thread:{0},开始做鱼,需要5秒钟", Thread.CurrentThread.Name);
Thread.Sleep(5000);
Console.WriteLine("Thread:{0},鱼做好了,好香", Thread.CurrentThread.Name);

resetEvent.Set();
}
}

ManualResetEventSlim

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
public static void TestManualResetEventSlim() {
Thread t1 = new Thread(() => TravelThroughGates("Thread1", 5));
Thread t2 = new Thread(() => TravelThroughGates("Thread2", 6));
Thread t3 = new Thread(() => TravelThroughGates("Thread3", 7));

t1.Start();
t2.Start();
t3.Start();

Thread.Sleep(TimeSpan.FromSeconds(6));

Console.WriteLine("The gates are now open!");
_mainEvent.Set();

Thread.Sleep(TimeSpan.FromSeconds(1));
_mainEvent.Reset();
Console.WriteLine("The gates have been closed!");

Thread.Sleep(TimeSpan.FromSeconds(10));
Console.WriteLine("The gates are now open for the second!");

_mainEvent.Set();
Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("The gates have been closed!");

_mainEvent.Reset();
}

private static ManualResetEventSlim _mainEvent = new ManualResetEventSlim(false);
public static void TravelThroughGates(string threadName, int seconds) {
Console.WriteLine("{0} falls to sleep", threadName);
Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine("{0} waits for the gates to open", threadName);

_mainEvent.Wait();

Console.WriteLine("{0} enters the gates", threadName);
}
类似人群通过大门,set()大门打开,reset()大门关闭

CountDownEvent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void TestCountdownEvent() {
Thread t1 = new Thread(() => PerformOperation("Operation 1 is completed!", 4));
Thread t2 = new Thread(() => PerformOperation("Operation 2 is completed!", 8));

t1.Start();
t2.Start();

_countdown.Wait();

Console.WriteLine("Both operation have been completed!");
_countdown.Dispose();
}

private static CountdownEvent _countdown = new CountdownEvent(2);

private static void PerformOperation(string msg, int seconds) {
Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine(msg);
_countdown.Signal();
}
如果调用 _countdown.Signal() 没达到指定次数,_countdown.Wait() 将会一直等待,请确保使用 CountdownEvent 时,所有线程完成后都要调用 Signal()

Barrier

Barrier(a, b)
a -> 调用 SignalAndWait() 的次数
b -> 回调函数    
SignalAndWait() 调用 a 次后,立即执行 b 回调函数
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
public static void TestBarrier() {
Thread t1 = new Thread(() => PlayMusic("侧脸!", "于果", 5));
Thread t2 = new Thread(() => PlayMusic("万物生!", "沙丁丁", 2));

Thread t3 = new Thread(() => PlayMusic("岁月神偷!", "金玟岐", 6));
Thread t4 = new Thread(() => PlayMusic("老谁小谁!", "天堂乐队", 4));

t1.Start();
t2.Start();
t3.Start();
t4.Start();
}

private static void PlayMusic(string name, string msg, int seconds) {
Barrier _barrier = new Barrier(2, b => Console.WriteLine("End of phase {0}",
b.CurrentPhaseNumber + 1));

Console.WriteLine("------------------------------");
Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine("{0} starts to {1}", name, msg);

Thread.Sleep(TimeSpan.FromSeconds(seconds));
Console.WriteLine("{0} starts to {1}", name, msg);

_barrier.SignalAndWait();
}

ReaderWriterLockSlim

读取器/编写器锁,允许多个线程同时读取一个资源,但在向该资源写入时要求线程等待以获得独占锁

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
public static void TestReaderWriterLockSlim() {
new Thread(Read) { IsBackground = true }.Start();
new Thread(Read) { IsBackground = true }.Start();
new Thread(Read) { IsBackground = true }.Start();

new Thread(() => Write("Thread 1")) { IsBackground = true }.Start();
new Thread(() => Write("Thread 2")) { IsBackground = true }.Start();

Thread.Sleep(TimeSpan.FromSeconds(30));
}

private static ReaderWriterLockSlim _rw = new ReaderWriterLockSlim();
private static Dictionary<int, int> _items = new Dictionary<int, int>();

private static void Read() {
Console.WriteLine("Reading contents of a dictornary");
while (true) {
try
{
_rw.EnterReadLock();
foreach (var key in _items.Keys) {
Thread.Sleep(TimeSpan.FromSeconds(0.2));
}
}
finally {
_rw.ExitReadLock();
}
}
}
private static void Write(string threadName)
{
while (true)
{
try
{
int intNewKey = new Random().Next(250);
_rw.EnterUpgradeableReadLock();
if (!_items.ContainsKey(intNewKey)) {
try
{
_rw.EnterWriteLock();
_items[intNewKey] = 1;
Console.WriteLine("new key {0} is added to a dictionary by a {1}",
intNewKey, threadName);
}
finally {
_rw.ExitWriteLock();
}
}
Thread.Sleep(TimeSpan.FromSeconds(0.1));
}
finally
{
_rw.ExitUpgradeableReadLock();
}
}
}
  • 构造一个线程安全的缓存
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/// <summary>
/// 同步Cache
/// </summary>
public class SynchronizedCache
{
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
private Dictionary<int, string> innerCache = new Dictionary<int, string>();
/// <summary>
/// 读取
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string Read(int key)
{
cacheLock.EnterReadLock();
try
{
return innerCache[key];
}
finally
{
cacheLock.ExitReadLock();
}
}
/// <summary>
/// 添加项
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void Add(int key, string value)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
}
/// <summary>
/// 添加项,有超时限制
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="timeout"></param>
/// <returns></returns>
public bool AddWithTimeout(int key, string value, int timeout)
{
if (cacheLock.TryEnterWriteLock(timeout))
{
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return true;
}
else
{
return false;
}
}
/// <summary>
/// 添加或者更新
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public AddOrUpdateStatus AddOrUpdate(int key, string value)
{
cacheLock.EnterUpgradeableReadLock();
try
{
string result = null;
if (innerCache.TryGetValue(key, out result))
{
if (result == value)
{
return AddOrUpdateStatus.Unchanged;
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache[key] = value;
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Updated;
}
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Added;
}
}
finally
{
cacheLock.ExitUpgradeableReadLock();
}
}
/// <summary>
/// 删除项
/// </summary>
/// <param name="key"></param>
public void Delete(int key)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Remove(key);
}
finally
{
cacheLock.ExitWriteLock();
}
}
/// <summary>
///
/// </summary>
public enum AddOrUpdateStatus
{
Added,
Updated,
Unchanged
};
}

SpinWait 类

轻量同步类型,可以在低级别方案中使用它来避免内核事件所需的高开销的上下文切换和内核转换。 在多核计算机上,当预计资源不会保留很长一段时间时,如果让等待线程以用户模式旋转数十或数百个周期,然后重新尝试获取资源,则效率会更高。 如果在旋转后资源变为可用的,则可以节省数千个周期。 如果资源仍然不可用,则只花费了少量周期,并且仍然可以进行基于内核的等待。 这一旋转-等待的组合有时称为“两阶段等待操作”。

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
public static void TestSpinWait() {
Console.Write("Running UserModeWait");
new Thread(UserModeWait).Start();
Thread.Sleep(20);
_isCompleted = true;
Thread.Sleep(TimeSpan.FromSeconds(1));
_isCompleted = false;
Console.Write("Running HybridSpinWait");

new Thread(HybridSpinWait).Start();
Thread.Sleep(20);
_isCompleted = true;
}

private static volatile bool _isCompleted = false;

private static void UserModeWait() {
while (!_isCompleted) {
Console.Write(".");
}
Console.WriteLine();
Console.WriteLine("Waiting is complete");
}
private static void HybridSpinWait()
{
SpinWait w = new SpinWait();
while (!_isCompleted)
{
w.SpinOnce();
Console.Write("\n" + w.NextSpinWillYield);
}
Console.WriteLine();
Console.WriteLine("Waiting is complete");
}
  • 无锁堆栈
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
public class LockFreeStack<T>
{
private volatile Node m_head;

private class Node { public Node Next; public T Value; }

public void Push(T item)
{
SpinWait spin = new SpinWait();
Node node = new Node { Value = item }, head;
while (true)
{
head = m_head;
node.Next = head;
if (Interlocked.CompareExchange(ref m_head, node, head) == head) break;
spin.SpinOnce();
}
}

public bool TryPop(out T result)
{
result = default(T);
SpinWait spin = new SpinWait();

Node head;
while (true)
{
head = m_head;
if (head == null) return false;
if (Interlocked.CompareExchange(ref m_head, head.Next, head) == head)
{
result = head.Value;
return true;
}
spin.SpinOnce();
}
}
}