参考网址: https://blog.csdn.net/weixin_43989331/article/details/105356008

C#中的几种锁:用户模式锁、内核模式锁、动态计数、监视锁
介绍几种C#中的锁,最常用的是最后的监视锁,其他的也有必要了解一下原理及应用,特别像WaitOne、WaitHandle在我们项目中应用还是挺多的。

文章目录
C#中的几种锁:用户模式锁、内核模式锁、动态计数、监视锁
用户模式锁
内核模式锁
动态计数锁
监视锁

锁:解决多线程中的数据共享安全问题。
用户模式锁
volatile关键字:取消release对底层的优化,在读写的时候都从内存中读取

SpinLock 旋转锁:

SpinLock spinLock = new SpinLock();
bool lockTaken = false;
spinLock.Enter(ref lockTaken);
spinLock.Exit();

内核模式锁
分为:事件锁、信号量、互斥锁、读写锁。

建议:通常不建议随便使用内核模式锁,资源付出相对较大。我们可以使用混合锁代替,以及我们马上讲到的lock关键字。

事件锁(自动事件锁、手动事件锁):

自动事件锁:AutoResetEvent

AutoResetEvent myLock = new AutoResetEvent(true);//true:表示终止状态(初始状态),false表示非终止
myLock.WaitOne();
//...
myLock.Set();

手动事件锁:ManualResetEvent,和自动事件锁相比,差距在于可以对多个变量进行批量锁

ManualResetEvent myLock = new ManualResetEvent(false);//true:可以正常通过的。false:拦截状态,禁止通过。

myLock.WaitOne();//批量拦截
//...//由于是一批,这里是无序的
myLock.Set();

Semaphore 信号量

基本原理:是通过int数值来控制线程的个数

Semaphore myLock = new Semaphore(5, 10);//第一个参数表示同时可以允许的线程数,第二个是最大值

Semaphore myLock = new Semaphore(1, 10);//每次只能一个线程通过

Semaphore myLock = new Semaphore(1, 10);

myLock.WaitOne();
//...
myLock.Release();

  • Mutex互斥锁

    可用于非全局变量互斥的情况,如同一ID的用户只允许提交一次抽奖请求。

Mutex mutex = new Mutex();

mutex.WaitOne();
//...
mutex.ReleaseMutex();
 

以上三种锁都有WaitOne()方法,因为他们都继承自waitHandle。

读写锁ReaderWriterLock

注意:读写锁并不是从限定线程个数的角度出发。而是按照读写的功能划分。

读写锁的基本方案:多个线程可以一起读,只能让一个线程去写。

模拟场景:多个线程读取,一个线程写。请思考:写的线程是否能够正常阻止读的线程?如果能阻止,则达到目标。

static ReaderWriterLock readerWriterLock = new ReaderWriterLock();

/// <summary>
/// 读取数据的线程
/// </summary>
private static void ThreadRead()
{
while (true)
{
readerWriterLock.AcquireReaderLock(int.MaxValue);//参数:表示最大的超时时间

Thread.Sleep(100);
Console.WriteLine($"当前读取的tid={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
readerWriterLock.ReleaseReaderLock();
}
}
/// <summary>
/// 写入数据的线程
/// </summary>
private static void ThreadWrite()
{
while (true)
{
readerWriterLock.AcquireWriterLock(int.MaxValue);//参数:表示最大的超时时间

Thread.Sleep(3000);
Console.WriteLine($"---------------------------------------------当前写入的tid={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");

readerWriterLock.ReleaseWriterLock();
}
}
//通过观察,我们发现写入的时候,能够正常的拦截读取的线程。
//PS:如果我们写入数据的任务耗时太长,比如十几秒或更长,此时读的线程会被卡死,从而超时。开发中要特别注意。

动态计数锁
CountdownEvent:限制线程数的一个机制,而且这个也是比较常用的(同属于信号量的一种).

使用场景:基于多个线程从某一个表中读取数据:比如我们现有A、B、C…每一张数据表我们都希望通过多个线程去读取。因为用一个线程的话,那么数据量大会出现卡死的情况。

举例:

A表:10w数据–》10个线程读取,1个线程1w条数据。
B表:5w数据 --》5个线程 1个线程1w
C表:1w数据 --》2个线程 1个线程5k

private static CountdownEvent countdownEvent = new CountdownEvent(10);
//默认10个threadcount初始值,一个线程用一个就减掉1,直到为0后,相当于结束
static void LoadData()
{
countdownEvent.Reset(10);//重置当前ThreadCount上限
for (int i = 0; i < 10; i++)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
LoadTableA();
});
}

//阻止当前线程,直到设置了System.Threading.CountdonwEvent为止
countdownEvent.Wait();//相当于Task.WaitAll()

Console.WriteLine("TableA加载完毕..........\r\n");

//加载B表
countdownEvent.Reset(5);
for (int i = 0; i < 5; i++)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
LoadTableB();
});
}
countdownEvent.Wait();
Console.WriteLine("TableB加载完毕..........\r\n");

//加载C表
myLock7.Reset(2);
for (int i = 0; i < 2; i++)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
LoadTableC();
});
}
countdownEvent.Wait();
Console.WriteLine("TableC加载完毕..........\r\n");
}

/// <summary>
/// 加载A表
/// </summary>
private static void LoadTableA()
{
//在这里编写具体的业务逻辑...
Console.WriteLine($"当前TableA正在加载中...{Thread.CurrentThread.ManagedThreadId}");
countdownEvent.Signal();//将当前的ThreadCount-- 操作,就是减掉一个值
}

/// <summary>
/// 加载B表
/// </summary>
private static void LoadTableB()
{
//在这里编写具体的业务逻辑...
Console.WriteLine($"当前TableB正在加载中...{ Thread.CurrentThread.ManagedThreadId}");
countdownEvent.Signal();
}

/// <summary>
/// 加载C表
/// </summary>
private static void LoadTableC()
{
//在这里编写具体的业务逻辑...
Console.WriteLine($"当前TableC正在加载中...{Thread.CurrentThread.ManagedThreadId}");
countdownEvent.Signal();
}

动态计数锁
CountdownEvent:限制线程数的一个机制,而且这个也是比较常用的(同属于信号量的一种).

使用场景:基于多个线程从某一个表中读取数据:比如我们现有A、B、C…每一张数据表我们都希望通过多个线程去读取。因为用一个线程的话,那么数据量大会出现卡死的情况。

举例:

A表:10w数据–》10个线程读取,1个线程1w条数据。
B表:5w数据 --》5个线程 1个线程1w
C表:1w数据 --》2个线程 1个线程5k

private static CountdownEvent countdownEvent = new CountdownEvent(10);
//默认10个threadcount初始值,一个线程用一个就减掉1,直到为0后,相当于结束
static void LoadData()
{
countdownEvent.Reset(10);//重置当前ThreadCount上限
for (int i = 0; i < 10; i++)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
LoadTableA();
});
}

//阻止当前线程,直到设置了System.Threading.CountdonwEvent为止
countdownEvent.Wait();//相当于Task.WaitAll()

Console.WriteLine("TableA加载完毕..........\r\n");

//加载B表
countdownEvent.Reset(5);
for (int i = 0; i < 5; i++)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
LoadTableB();
});
}
countdownEvent.Wait();
Console.WriteLine("TableB加载完毕..........\r\n");

//加载C表
myLock7.Reset(2);
for (int i = 0; i < 2; i++)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
LoadTableC();
});
}
countdownEvent.Wait();
Console.WriteLine("TableC加载完毕..........\r\n");
}

/// <summary>
/// 加载A表
/// </summary>
private static void LoadTableA()
{
//在这里编写具体的业务逻辑...
Console.WriteLine($"当前TableA正在加载中...{Thread.CurrentThread.ManagedThreadId}");
countdownEvent.Signal();//将当前的ThreadCount-- 操作,就是减掉一个值
}

/// <summary>
/// 加载B表
/// </summary>
private static void LoadTableB()
{
//在这里编写具体的业务逻辑...
Console.WriteLine($"当前TableB正在加载中...{ Thread.CurrentThread.ManagedThreadId}");
countdownEvent.Signal();
}

/// <summary>
/// 加载C表
/// </summary>
private static void LoadTableC()
{
//在这里编写具体的业务逻辑...
Console.WriteLine($"当前TableC正在加载中...{Thread.CurrentThread.ManagedThreadId}");
countdownEvent.Signal();
}

动态计数锁
CountdownEvent:限制线程数的一个机制,而且这个也是比较常用的(同属于信号量的一种).

使用场景:基于多个线程从某一个表中读取数据:比如我们现有A、B、C…每一张数据表我们都希望通过多个线程去读取。因为用一个线程的话,那么数据量大会出现卡死的情况。

举例:

A表:10w数据–》10个线程读取,1个线程1w条数据。
B表:5w数据 --》5个线程 1个线程1w
C表:1w数据 --》2个线程 1个线程5k

private static CountdownEvent countdownEvent = new CountdownEvent(10);
//默认10个threadcount初始值,一个线程用一个就减掉1,直到为0后,相当于结束
static void LoadData()
{
countdownEvent.Reset(10);//重置当前ThreadCount上限
for (int i = 0; i < 10; i++)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
LoadTableA();
});
}

//阻止当前线程,直到设置了System.Threading.CountdonwEvent为止
countdownEvent.Wait();//相当于Task.WaitAll()

Console.WriteLine("TableA加载完毕..........\r\n");

//加载B表
countdownEvent.Reset(5);
for (int i = 0; i < 5; i++)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
LoadTableB();
});
}
countdownEvent.Wait();
Console.WriteLine("TableB加载完毕..........\r\n");

//加载C表
myLock7.Reset(2);
for (int i = 0; i < 2; i++)
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
LoadTableC();
});
}
countdownEvent.Wait();
Console.WriteLine("TableC加载完毕..........\r\n");
}

/// <summary>
/// 加载A表
/// </summary>
private static void LoadTableA()
{
//在这里编写具体的业务逻辑...
Console.WriteLine($"当前TableA正在加载中...{Thread.CurrentThread.ManagedThreadId}");
countdownEvent.Signal();//将当前的ThreadCount-- 操作,就是减掉一个值
}

/// <summary>
/// 加载B表
/// </summary>
private static void LoadTableB()
{
//在这里编写具体的业务逻辑...
Console.WriteLine($"当前TableB正在加载中...{ Thread.CurrentThread.ManagedThreadId}");
countdownEvent.Signal();
}

/// <summary>
/// 加载C表
/// </summary>
private static void LoadTableC()
{
//在这里编写具体的业务逻辑...
Console.WriteLine($"当前TableC正在加载中...{Thread.CurrentThread.ManagedThreadId}");
countdownEvent.Signal();
}

C#中的几种锁:用户模式锁、内核模式锁、动态计数、监视锁的更多相关文章

  1. 【windows 操作系统】【CPU】用户模式和内核模式(用户层和内核层)

    所有的现代操作系统中,CPU是在两种不同的模式下运行的: 注意以下内容来自微软: windows用户模式和内核模式 运行 Windows 的计算机中的处理器有两个不同模式:用户模式 和内核模式 . 用 ...

  2. 理解Windows内核模式与用户模式

     1.基础 执行 Windows 的计算机中的处理器有两个不同模式:"用户模式"和"内核模式". 依据处理器上执行的代码的类型,处理器在两个模式之间切换.应 ...

  3. Windows系统的四个重要概念——进程、线程、虚拟内存、内核模式和用户模式

    引言 本来在写一篇Windows内存管理的文章,写着写着就发现好多基础的概念都要先讲.更可怕的是,这些基础的概念我却不能完全讲清楚.只好再把这本<深入解析Windows操作系统>翻到第一章 ...

  4. 如何看待Linux操作系统的用户空间和内核空间

    作为中央核心处理单元的CPU,除了生产工艺的不断革新进步外,在处理数据和响应速度方面也需要有权衡.稍有微机原理基础的人都知道Intel X86体系的CPU提供了四种特权模式ring0~ring3,其中 ...

  5. Linux 用户态和内核态

    1.特权级特权级用来管理和控制程序执行.如Intel x86架构的CPU,有0~3四个特权级,0级最高,3级最低.硬件在执行每条指令时都会检查指令具有的特权级.硬件提供了特权级使用机制,对操作系统来说 ...

  6. 监视锁——Java同步的基本思想

    翻译人员: 铁锚翻译时间: 2013年11月13日原文链接: Monitors – The Basic Idea of Java synchronization如果你上过操作系统课程,你就知道监视锁( ...

  7. 使用WinDbg调试入门(内核模式)

    windbg是一个内核模式和用户模式调试器,包含在Windows调试工具中.这里我们提供了一些实践练习,可以帮助您开始使用windbg作为内核模式调试器. 设置内核模式调试 内核模式调试环境通常有两台 ...

  8. 内核模式构造-Event构造(WaitLock)

    internal sealed class SimpleWaitLock:IDisposable { //Enter()和Leave()中使用m_AutoResetEvent都将迫使调用线程做用户模式 ...

  9. 全面了解Java中的15种锁概念及机制!

    在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 1.公平锁 / 非公平锁 2.可重入锁 / 不可重入锁 3.独享锁 / 共享锁 4.互斥锁 / 读 ...

随机推荐

  1. ctf之SusCTF2017-Crack Zip

    题目信息如下,可知为杂项题,且无提示 下载文件打开如图,该压缩包是加密的 首先想到的是暴力破解,下载zip暴力破解软件打开文件. 下一步,选择暴力破解 进行暴力破解设定,进行破解 破解完成,得到密解压 ...

  2. python 遍历文件夹中所有文件

    '''使用walk方法递归遍历目录文件,walk方法会返回一个三元组,分别是root.dirs和files. 其中root是当前正在遍历的目录路径:dirs是一个列表,包含当前正在遍历的目录下所有的子 ...

  3. IE浏览器查看星号密码

    用CHROME打开保存密码的网页,F12,右击"密码框"检查,编辑属性:password改为passw(只要不是password即可)即可显示密码

  4. react-router4 介绍

    react-router 的理解: 1) react 的一个插件库2) 专门用来实现一个 SPA 应用3) 基于 react 的项目基本都会用到此库 SPA  的理解: 1) 单页 Web 应用(si ...

  5. oracle 密码详解以及破解

    参考的相关资料等: https://docs.oracle.com/en/database/oracle/oracle-database/18/spmsu/finding-and-resetting- ...

  6. Linux虚拟机扩展磁盘

    Linux虚拟机扩展磁盘 1.虚拟机关机,Vmware中扩展磁盘 2.虚拟机开机,查看磁盘大小 [root@hadoop6 ~]# df -h 文件系统 容量 已用 可用 已用% 挂载点 devtmp ...

  7. NOIp 2020

    游记 Day-1 我已经开始慌了. 不知道前路如何.不想回文化课.唯一一次机会,可是这几天却一直在颓,不颓就慌. 没心思写题,导致这几天看的题啥都不会.不知道考试当天又会出什么幺蛾子. 啊啊啊,烦. ...

  8. K8S系列第三篇(Docker网络)

    目录 docker 网络 Docker 的四种网络模 一.网络基础 1.网络名称空间介绍 2.创建一个命名空间 1)Veth设备对 2)Veth设备操作 1> 创建Veth设备对 2> 绑 ...

  9. java类与对象基础篇

    java面向对象基础篇 面向对象程序设计(Object Oriented Proframming ,OOP) 面向对象的本质是:以类的方式组织代码,以对象的方式组织(封装)数据. 面向对象的核心思想是 ...

  10. dotnet 是 前30个增长最快速度的开源项目中排名第一的开发平台

    CNCF 的博客 发了一篇文章 <Update on CNCF and Open Source Project Velocity 2020>,中文翻译参见 2020年CNCF和开源项目开发 ...