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

一. 整体介绍

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

  内核模式锁包括:

    ①:事件锁

    ②:信号量

    ③:互斥锁

    ④:读写锁

    ⑤:动态锁

 

二. 事件锁

 事件锁包括:

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         }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438332.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

玩转Mybatis —— 一个小demo,带你快速入门Mybatis

目录 &#x1f91e;Mybatis官网介绍 &#x1f91e;Mybatis安装 &#x1f91e;Mybatis核心配置文件 &#x1f91e;构建 SqlSessionFactory &#x1f91e;获取 SqlSession &#x1f91e;通过 XML 定义已映射的 SQL 语句 &#x1f91e;作用域&#xff08;Scope&#xff09;…

MySQL掌握的怎么样?听说这几道MySQL面试题没有几个人能答得出来

MySQL可谓是程序员必备技能&#xff0c;怎么检测自己掌握了多少呢&#xff0c;一起来测试一下吧&#xff01;一共12个关卡&#xff0c;看看你能闯到第几关&#xff1f; 目录 什么是左外链接&#xff0c;什么是右外链接&#xff1f; 分页关键字是什么&#xff1f; Select 语句…

IIS/ASP.NET 管道

ASP.NET MVC 是建立在 ASP.NET 平台上基于 MVC 模式的 Web 应用框架&#xff0c;深刻理解 ASP.NET MVC 的前提是对 ASP.NET 管道式设计具有深刻的认识。由于 ASP.NET Web 应用大都寄宿于 IIS 上&#xff0c;将两者结合起来了解在 IIS 和 ASP.NET 管道中是如何流动的。 IIS5.x与…

【JAVA知识点每日一练】 —— 运算符的那些事

都说基础不牢地动山摇&#xff0c;还真是那么回事&#xff01;别小看这运算符&#xff0c;每个语法中运算符扮演者举足轻重的角色&#xff0c;稍微没用对&#xff0c;那可就犯了大错误&#xff01; 接下来跟随小梦&#xff0c;迈着轻松且愉快的步伐一起看看运算符的那些事吧~ 运…

ASP.NET Core管道深度剖析[共4篇]

在《管道是如何处理HTTP请求的&#xff1f;》中&#xff0c;我们对ASP.NET Core的请求处理管道的构成以及它对请求的处理流程进行了详细介绍&#xff0c;接下来我们需要了解的是这样一个管道是如何被构建起来的。这样一个管道由一个服务器和一个HttpApplication构成&#xff0c…

SQL为什么动不动就百行以K记?

发明SQL的初衷之一显然是为了降低人们实施数据查询计算的难度。SQL中用了不少类英语的词汇和语法&#xff0c;这是希望非技术人员也能掌握。确实&#xff0c;简单的SQL可以当作英语阅读&#xff0c;即使没有程序设计经验的人也能运用。 然而&#xff0c;面对稍稍复杂的查询计算…

深入理解happens-before和as-if-serial语义

本文大部分整理自《Java并发编程的艺术》&#xff0c;温故而知新&#xff0c;加深对基础的理解程度。下面可以和小编来一起学习下 概述 本文大部分整理自《Java并发编程的艺术》&#xff0c;温故而知新&#xff0c;加深对基础的理解程度。 指令序列的重排序 我们在编写代码…

开源SPL重新定义OLAP Server

OLAP&#xff08;Online Analytical Processing&#xff09;是指在线联机分析&#xff0c;基于数据查询计算并实时获得返回结果。日常业务中的报表、数据查询、多维分析等一切需要即时返回结果的数据查询任务都属于OLAP的范畴。对应的&#xff0c;行业内也有相应产品来满足这类…

平层、错层、跃层、复式、loft的区别是什么?

平层正如字面上的理解&#xff0c;是所有功能厅都在同一个水平面上。平时我们所见的户型&#xff0c;都是平层。错层室内各功能用房在不同的平面上&#xff0c;用2-4个台阶进行隔断。跃层是两层的住宅&#xff0c;在室内会设计一条楼梯连接上下两层&#xff0c;功能区会分开。复…

C#中集合接口关系笔记

IEnumerable IEnumerable接口是所有集合类型的祖宗接口&#xff0c;其作用相当于Object类型之于其它类型。如果某个类型实现了IEnumerable接口&#xff0c;就意味着它可以被迭代访问&#xff0c;也就可以称之为集合类型&#xff08;可枚举&#xff09;&#xff1b; ICollecti…

C#集合类型总结和性能分析

C#集合类型概述 集合是.NET FCL(Framework Class Library)中很重要的一部分。所有的集合类都继承自IEnumerable。集合类总体可分为一下几类&#xff1a;关联/非关联型集合&#xff0c;顺序/随机访问集合&#xff0c;顺序/无序集合&#xff0c;泛型/非泛型集合&#xff0c;线程…

为什么TypedReference在幕后

我发现的其他用途TypedReference: C#中的“专门化”泛型(这是类型安全的)&#xff1a; static void foo<T>(ref T value) {//This is the ONLY way to treat value as int, without boxing/unboxing objectsif (value is int){ __refvalue(__makeref(value), int) 1; …

C# 中的可变参数方法(VarArgs)

首先需要明确一点&#xff1a;这里提到的可变参数方法&#xff0c;指的是具有 CallingConventions.VarArgs 调用约定的方法&#xff0c;而不是包含 params 参数的方法。可以通过MethodBase.CallingConvention 属性来获取某个方法的调用约定。 举个常见的例子来说&#xff0c;C…

Spring Boot Actuator监控关闭

可以使用如下属性:management.endpoints.enabled-by-defaultfalse * 在YAML中有特殊的含义&#xff0c;所以如果想使用include或者exclude包含所有的端点时要加上引号&#xff0c;如下示例&#xff1a; # 暴露监控端点 management:endpoints:enabled-by-default: false #关闭…

Javascript获取类名方法

函数&#xff1a; entity.getClassName function(obj) { if (obj && obj.constructor && obj.constructor.toString()) { if(obj.constructor.name) { return obj.constructor.name; } let str…

mysql把一个数据库中的数据复制到另一个数据库中的表 2个表结构相同

1。表结构相同的表&#xff0c;且在同一数据库&#xff08;如&#xff0c;table1,table2) Sql &#xff1a;insert into table1 select * from table2 (完全复制) insert into table1 select distinct * from table2(不复制重复纪录&#xff09; insert into table1 select …

!Spring Aop中四个重要概念,切点,切面,连接点,通知

一、基本概念 1. 通知&#xff1a; 就是我们编写的希望Aop时额外执行的那个方法。我们通过Aop希望我们编写的方法在目标方法执行前执行&#xff0c;或者执行后执行。 2. 切点&#xff1a;切点就是我们配置的满足我们条件的目标方法。比如我们规定&#xff1a;名字前面是select…

Spring AOP(通知、连接点、切点、切面)

一、AOP术语 通知&#xff08;Advice&#xff09;   切面的工作被称为通知。通知定义了切面是什么以及何时使用。除了描述切面要完成的工作&#xff0c;通知还解决了何时执行这个工作的问题。 5种通知类型&#xff1a;前置通知&#xff08;Before&#xff09;&#xff1a;在…

Map集合根据key,value排序

/*** 根据map的key排序* * param map 待排序的map* param isDesc 是否降序&#xff0c;true&#xff1a;降序&#xff0c;false&#xff1a;升序* return 排序好的map*/public static <K extends Comparable<? super K>, V> Map<K, V> sortByKey(Map<K, …

C#中几种常用的集合的用法

集合:将一推数据类型相同的数据放入到一个容器内&#xff0c;该容器就是数组&#xff1a;内存中开辟的一连串空间。 非泛型集合 ArrayList集合&#xff1a; ArrayList是基于数组实现的&#xff0c;是一个动态数组&#xff0c;其容量能自动 增长 ArrayList的命名空间System.…