欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

第十二节:深究内核模式锁的使用场景(自动事件锁、手动事件锁、信号量、互斥锁、读写锁、动态锁)

发布时间:2023/12/10 编程问答 34 豆豆
生活随笔 收集整理的这篇文章主要介绍了 第十二节:深究内核模式锁的使用场景(自动事件锁、手动事件锁、信号量、互斥锁、读写锁、动态锁) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

一. 整体介绍

温馨提示:内核模式锁,在不到万不得已的情况下,不要使用它,因为代价太大了,有很多种替代方案。

  内核模式锁包括:

    ①:事件锁

    ②:信号量

    ③:互斥锁

    ④:读写锁

    ⑤:动态锁

 

二. 事件锁

 事件锁包括:

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 }

总结

以上是生活随笔为你收集整理的第十二节:深究内核模式锁的使用场景(自动事件锁、手动事件锁、信号量、互斥锁、读写锁、动态锁)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。