一. 整体介绍
温馨提示:内核模式锁,在不到万不得已的情况下,不要使用它,因为代价太大了,有很多种替代方案。
内核模式锁包括:
①:事件锁
②:信号量
③:互斥锁
④:读写锁
⑤:动态锁
二. 事件锁
事件锁包括:
A. 自动事件锁(AutoResetEvent)
使用场景:可以用此锁实现多线程环境下某个变量的自增.
现实场景: 进站火车闸机,我们用火车票来实现进站操作.
true: 表示终止状态,闸机中没有火车票
false: 表示非终止状态,闸机中此时有一张火车票
B.手动事件锁(ManualResetEvent)
现实场景:有人看守的铁道栅栏(和自动事件锁不一样,不能混用)
true: 栅栏没有合围,没有阻止行人通过铁路
false:栅栏合围了, 阻止行人通过
* 下面案例发现,锁不住,自增仍然是无序的输出了.
* 核心方法:WaitOne和Set
代码实践-自动事件锁:
1 static AutoResetEvent autoResetLock1 = new AutoResetEvent(true);2 static AutoResetEvent autoResetLock2 = new AutoResetEvent(false);3 static int num2 = 0;4 {5 //1. 能输出6 {7 autoResetLock1.WaitOne();8 Console.WriteLine("autoResetLock1检验通过,可以通行");9 autoResetLock1.Set();
10 }
11
12 //2. 不能输出
13 {
14 autoResetLock2.WaitOne();
15 Console.WriteLine("autoResetLock2检验通过,可以通行");
16 autoResetLock2.Set();
17 }
18
19 //3.下面代码的结果:num从0-249,有序的发现可以锁住。
20 {
21 for (int i = 0; i < 5; i++)
22 {
23 Task.Factory.StartNew(() =>
24 {
25 for (int j = 0; j < 50; j++)
26 {
27 try
28 {
29 autoResetLock1.WaitOne();
30 Console.WriteLine(num2++);
31 autoResetLock1.Set();
32 }
33 catch (Exception ex)
34 {
35 Console.WriteLine(ex.Message);
36 }
37
38 }
39 });
40 }
41 }
42 }
代码实践-手动事件锁:
1 static int num2 = 0;2 static ManualResetEvent mreLock = new ManualResetEvent(true);3 //下面代码锁不住,仍然是无序的输出了4 {5 for (int i = 0; i < 5; i++)6 {7 Task.Factory.StartNew(() =>8 {9 for (int j = 0; j < 50; j++)
10 {
11 try
12 {
13 mreLock.WaitOne();
14 Console.WriteLine(num2++);
15 mreLock.Set();
16 }
17 catch (Exception ex)
18 {
19 Console.WriteLine(ex.Message);
20 }
21
22 }
23 });
24 }
25 }
三. 信号量
信号量:
* 核心类:Semaphore,通过int数值来控制线程个数。
* 通过观察构造函数 public Semaphore(int initialCount, int maximumCount);:
* initialCount: 可以同时授予的信号量的初始请求数。
* maximumCount: 可以同时授予的信号量的最大请求数。
* static Semaphore seLock = new Semaphore(1, 1); //表示只允许一个线程通过
* 下面的案例可以有序的输出。
* 核心方法:WaitOne和Release
代码实践:
1 static Semaphore seLock = new Semaphore(1, 1); //只允许一个线程通过 2 //下面代码锁住了,可以有序的输出3 {4 for (int i = 0; i < 5; i++)5 {6 Task.Factory.StartNew(() =>7 {8 for (int j = 0; j < 50; j++)9 {
10 try
11 {
12 seLock.WaitOne();
13 Console.WriteLine(num2++);
14 seLock.Release();
15 }
16 catch (Exception ex)
17 {
18 Console.WriteLine(ex.Message);
19 }
20
21 }
22 });
23 }
24 }
四. 互斥锁
互斥锁:
核心方法:WaitOne和ReleaseMutex
下面案例可以锁住,有序输出
总结以上三种类型的锁,都有一个WaitOne方法,观察源码可知,都继承于WaitHandle类。
代码实践:
1 static Mutex mutex = new Mutex();2 //下面代码锁住了,可以有序的输出3 {4 for (int i = 0; i < 5; i++)5 {6 Task.Factory.StartNew(() =>7 {8 for (int j = 0; j < 50; j++)9 {
10 try
11 {
12 mutex.WaitOne();
13 Console.WriteLine(num2++);
14 mutex.ReleaseMutex();
15 }
16 catch (Exception ex)
17 {
18 Console.WriteLine(ex.Message);
19 }
20
21 }
22 });
23 }
24 }
五. 读写锁
读写锁(ReaderWriterLock):
背景:多个线程读,一个线程写,如果写入的时间太久,此时读的线程会被卡死,这个时候就要用到读写锁了。
锁读的两个核心方法:AcquireReaderLock和ReleaseReaderLock。
锁写的两个核心方法:AcquireWriterLock和ReleaseWriterLock。
代码实践:
1 static ReaderWriterLock rwlock = new ReaderWriterLock();2 private void button24_Click(object sender, EventArgs e)3 {4 #region 01-读写锁5 {6 //开启5个线程执行读操作7 for (int i = 0; i < 5; i++)8 {9 Task.Run(() =>
10 {
11 Read();
12 });
13 }
14 //开启1个线程执行写操作
15 Task.Factory.StartNew(() =>
16 {
17 Write();
18 });
19 }
20 #endregion
21
22 }
23 /// <summary>
24 /// 线程读
25 /// </summary>
26 static void Read()
27 {
28 while (true)
29 {
30 Thread.Sleep(10);
31 rwlock.AcquireReaderLock(int.MaxValue);
32 Console.WriteLine("当前 t={0} 进行读取 {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
33 rwlock.ReleaseReaderLock();
34 }
35 }
36 /// <summary>
37 /// 线程写
38 /// </summary>
39 static void Write()
40 {
41 while (true)
42 {
43 Thread.Sleep(300);
44 rwlock.AcquireWriterLock(int.MaxValue);
45 Console.WriteLine("当前 t={0} 进行写入 {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
46 rwlock.ReleaseWriterLock();
47 }
48 }
六. 动态锁
动态锁(CountdownEvent):
* 作用:限制线程数的一个机制。
* 业务场景:有Orders、Products、Users表,我们需要多个线程从某一张表中读取数据。
* 比如:Order表10w,10个线程读取。(每个线程读1w)
Product表5w,5个线程读取。(每个线程读1w)
User表2w,2个线程读取。(每个线程读1w)
三个核心方法:
①.Reset方法:重置当前的线程数量上限。(初始化的时候,默认设置一个上限)
②.Signal方法:将当前的线程数量执行减1操作。(使用一个thread,这个线程数量就会减1操作,直到为0后,继续下一步)
③.Wait方法:相当于我们的Task.WaitAll方法。
代码实践:
1 //初始化线程数量上限为10.2 static CountdownEvent cdLock = new CountdownEvent(10);3 private void button25_Click(object sender, EventArgs e)4 {5 //加载Orders搞定6 cdLock.Reset(10);7 for (int i = 0; i < 10; i++)8 {9 Task.Factory.StartNew(() =>
10 {
11 LoadOrder();
12 });
13 }
14 cdLock.Wait();
15 Console.WriteLine("所有的Orders都加载完毕。。。。。。。。。。。。。。。。。。。。。");
16
17 //加载Product搞定
18 cdLock.Reset(5);
19 for (int i = 0; i < 5; i++)
20 {
21 Task.Run(() =>
22 {
23 LoadProduct();
24 });
25 }
26 cdLock.Wait();
27 Console.WriteLine("所有的Products都加载完毕。。。。。。。。。。。。。。。。。。。。。");
28
29 //加载Users搞定
30 cdLock.Reset(2);
31 for (int i = 0; i < 2; i++)
32 {
33 Task.Factory.StartNew(() =>
34 {
35 LoadUser();
36 });
37 }
38 cdLock.Wait();
39 Console.WriteLine("所有的Users都加载完毕。。。。。。。。。。。。。。。。。。。。。");
40
41 Console.WriteLine("所有的表数据都执行结束了。。。恭喜恭喜。。。。");
42 Console.Read();
43 }
44 static void LoadOrder()
45 {
46 //书写具体的业务逻辑
47 Console.WriteLine("当前LoadOrder正在加载中。。。{0}", Thread.CurrentThread.ManagedThreadId);
48 //线程数量减1
49 cdLock.Signal();
50
51 }
52 static void LoadProduct()
53 {
54 //书写具体的业务逻辑
55 Console.WriteLine("当前LoadProduct正在加载中。。。{0}", Thread.CurrentThread.ManagedThreadId);
56 //线程数量减1
57 cdLock.Signal();
58 }
59 static void LoadUser()
60 {
61 //书写具体的业务逻辑
62 Console.WriteLine("当前LoadUser正在加载中。。。{0}", Thread.CurrentThread.ManagedThreadId);
63 //线程数量减1
64 cdLock.Signal();
65 }