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

一. 整体介绍

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

  内核模式锁包括:

    ①:事件锁

    ②:信号量

    ③:互斥锁

    ④:读写锁

    ⑤:动态锁

 

二. 事件锁

 事件锁包括:

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#集合类型总结和性能分析

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

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

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

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

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

C#使用Redis的基本操作

一&#xff0c;引入dll 1.ServiceStack.Common.dll 2.ServiceStack.Interfaces.dll 3.ServiceStack.Redis.dll 4.ServiceStack.Text.dll 二&#xff0c;修改配置文件 在你的配置文件中加入如下的代码&#xff1a; <appSettings><add key"RedisPath" value…

Navicat将mysql表结构导成oracle表结构

1&#xff0c;选中对应的表右键逆向表到模型 2.点击右上角文件转换模型为 3.模型选择物理&#xff0c;数据库oracle&#xff0c;选择对应的版本 4.新弹出的模型点击右上角文件&#xff0c;导出sql 5.选择路径导出sql

程序员们的三高:高并发、高性能、高可用

你们知道淘宝&#xff0c;京东这些购物商场吗&#xff1f;他们到了双11&#xff0c;双12为什么能支持全国14亿人口同时购物下单呢&#xff0c;因为他们的程序做到了高并发、高性能、高可用。那么你对程序员的三高了解多少呢&#xff1f; 高并发 一. 高并发 高并发是现在互联…

char 和 varchar 的区别,数据库索引B+树

char 和 varchar 的区别 char(n) &#xff1a;固定长度&#xff0c;效率高&#xff1b;缺点&#xff1a;占用空间&#xff1b;存储固定长度的&#xff0c;使用 char 非常合适。 varchar(n) &#xff1a;可变长度&#xff0c;存储的值是每个值占用的字节再加上一个用来记录其长…

C#基础操作符详解

本节内容&#xff1a; 1.操作符概览&#xff1b; 2.操作符的本质&#xff1b; 3.操作符与运算顺序 4.操作符详解。 1.操作符概览&#xff1a; 操作符&#xff08;Operator&#xff09;也译为”运算符” 操作符是用来操作数据的&#xff0c;被操作符操作的数据称为操作数&a…

C# 有什么惊艳到你的地方?

作者&#xff1a;皮皮关 链接&#xff1a;https://www.zhihu.com/question/335137780/answer/786853293 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 很多游戏开发者都是由于Unity而“被迫”使用C#的。但用过一段…

SqlServer学习之存储过程

前言&#xff1a;对于存储过程一直有一种抵触的心理&#xff0c;因为毕业至今所在的公司开发组都不是很规范&#xff0c;对于开发的一些注意事项并没有很多的规定&#xff0c;只是在知乎上查找相关知识的时候&#xff0c;看到很多人对于在程序里使用存储过程的不好之处都有很多…

中间件之RPC

一、RPC的定义 1、RPC(Romote Procedure Call)&#xff1a;远程过程调用&#xff0c;允许一台计算机程序远程调用另外一台计算机的子程序&#xff0c;不用关心底层网络通信 2、应用&#xff1a;分布式网络通信 3、在Socket的基础上实现&#xff0c;比socket需要更多资源 4、…