一、前言

  本文章汇总c#中常见的锁,基本都列出了该锁在微软官网的文章,一些不常用的锁也可以参考微软文章左侧的列表,方便温习回顾。

二、锁的分类

2.1、用户模式锁

  1、volatile 关键字

  volatile 并没有实现真正的线程同步,操作级别停留在变量级别并非原子级别,对于单系统处理器中,变量存储在主内存中,没有机会被别人修改。但是如果是多处理器,可能就会有问题,因为每个处理器都有单独的data cache,数据更新不一定立刻被写回到主存,可能会造成不同步。

参考:valatile 微软官网文章。

  2、Spinlock 旋转锁

  Spinlock 是内核中提供的一种比较常见的锁机制,自旋锁是“原地等待”的方式解决资源冲突的,即,一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁则获取不到,只能够原地“打转”(忙等待)。由于自旋锁的这个忙等待的特性,注定了它使用场景上的限制 :自旋锁不应该被长时间的持有(消耗 CPU 资源)。

参考:Spinlock 微软官网文章。

2.2、内核模式锁

  1、事件锁

  自动事件锁:AutoResetEvent

  WaitOne()进入等待,Set()会释放当前锁给一个等待线程。

var are = new AutoResetEvent(true);
are.WaitOne();
//...
are.Set();

  手动事件锁:ManualResetEvent

  WaitOne()进入等待,Set()会释放当前锁给所有等待线程。

var mre = new ManualResetEvent(false);

mre.WaitOne();//批量拦截,后续的省略号部分是无序执行的。
//...
mre.Set();//一次释放给所有等待线程

参考:ManuaResetEvent 微软官网文章。

  2、信号量

  信号量:Semaphore

  信号量可以控制同时通过的线程数以及总的线程数。

//第一个参数表示同时可以允许的线程数,比如1表示每次只允许一个线程通过,
//第二个是最大值,比如8表示最多有8个线程。
var semaphore = new Semaphore(1, 8);

参考:Semaphore 微软官网文章。

  3、互斥锁

  互斥锁:Mutex

  Mutex和Monitor很接近,但是没有Monitor.Pulse,Wait,PulseAll的唤醒功能,他的优点是可以跨进程,可以在同一台机器甚至远程机器人的不同进程间共用一个互斥体。

var mutex = new Mutex();
mutex.WaitOne();
//...
mutex.ReleaseMutex();

参考:Mutex 微软官网文章。

  4、读写锁

  读写锁:ReaderWriterLock

  不要使用ReaderWriterLock,该类有问题(死锁、性能),请使用ReaderWriterLockSlim

.NET Framework有两个读取器-编写器锁,ReaderWriterLockSlim以及ReaderWriterLock。 建议对所有新开发的项目使用 ReaderWriterLockSlim。 虽然 ReaderWriterLockSlim 类似于 ReaderWriterLock,但不同之处在于,前者简化了递归规则以及锁状态的升级和降级规则。 ReaderWriterLockSlim 避免了许多潜在的死锁情况。 另外,ReaderWriterLockSlim 的性能显著优于 ReaderWriterLock。

参考:ReaderWriterLock 微软官网文章。

  

  读写锁:ReaderWriterLockSlim

//源码摘录自微软官网
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic; public class SynchronizedCache
{
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
private Dictionary<int, string> innerCache = new Dictionary<int, string>(); public int Count
{ get { return innerCache.Count; } } public string Read(int key)
{
cacheLock.EnterReadLock();
try
{
return innerCache[key];
}
finally
{
cacheLock.ExitReadLock();
}
} public void Add(int key, string value)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
} public bool AddWithTimeout(int key, string value, int timeout)
{
if (cacheLock.TryEnterWriteLock(timeout))
{
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return true;
}
else
{
return false;
}
} public AddOrUpdateStatus AddOrUpdate(int key, string value)
{
cacheLock.EnterUpgradeableReadLock();
try
{
string result = null;
if (innerCache.TryGetValue(key, out result))
{
if (result == value)
{
return AddOrUpdateStatus.Unchanged;
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache[key] = value;
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Updated;
}
}
else
{
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Added;
}
}
finally
{
cacheLock.ExitUpgradeableReadLock();
}
} public void Delete(int key)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Remove(key);
}
finally
{
cacheLock.ExitWriteLock();
}
} public enum AddOrUpdateStatus
{
Added,
Updated,
Unchanged
}; ~SynchronizedCache()
{
if (cacheLock != null) cacheLock.Dispose();
}
}

ReaderWriterLockSlim示例

参考:ReaderWriterLockSlim 微软官网文章。

2.3、动态计数锁

  1、动态计数锁:CountdownEvent

  限制线程数的一个机制,而且这个也是比较常用的(同属于信号量的一种)。

var cde = new CountdownEvent(10);

//重置当前ThreadCount上限
cde.Reset(10);
for(int i=0; i<10; i++)
{
Task.Factory.StartNew(()=>
{
Thread.Sleep(1000);
SubWoker1();
});
} cde.Wait();//相当于Task.WaitAll() cde.Reset(8);
for(int i=0; i<8; i++)
{
Task.Factory.StartNew(()=>
{
Thread.Sleep(1000);
SubWoker2();
});
}
cde.Wait();//相当于Task.WaitAll() static void SubWoker1()
{
//...
cde.Signal();//将当前的ThreadCount-1操作。
} static void SubWoker2()
{
//...
cde.Signal();//将当前的ThreadCount-1操作。
}

CountdownEvent示例

参考:CountdownEvent 微软官网文章。

  2、原子操作类:Interlocked

  Interlocked类则提供了4种方法进行原子级别的变量操作。Increment , Decrement , Exchange 和CompareExchange 。

  a、使用Increment 和Decrement 可以保证对一个整数的加减为一个原子操作。

  b、Exchange 方法自动交换指定变量的值。

  c、CompareExchange 方法组合了两个操作:比较两个值以及根据比较的结果将第三个值存储在其中一个变量中。

  d、比较和交换操作也是按原子操作执行的。Interlocked.CompareExchange(ref a, b, c); 原子操作,a参数和c参数比较, 相等b替换a,不相等不替换。

参考:Interlocked 微软官网文章。

2.4、监视锁

  1、监视锁:Monitor

  Monitor锁为操作的代码块添加互斥对象,如果A线程正在访问,对象没有到达临界区,则B线程不会访问。

参考:Monitor 微软官网文章。

  2、监视锁:lock

  lock锁可以视为monitor锁的语法糖,增加了自动释放机制和异常处理机制。

  a、不推荐使用lock(this)的方式作为lock锁,因为你不确定别的地方是否重新实例了含有lock的对象。

  b、不要lock一个字符串。

  c、不要lock一个外部公开变量。

C# 锁汇总的更多相关文章

  1. 为什么说JAVA中要慎重使用继承 C# 语言历史版本特性(C# 1.0到C# 8.0汇总) SQL Server事务 事务日志 SQL Server 锁详解 软件架构之 23种设计模式 Oracle与Sqlserver:Order by NULL值介绍 asp.net MVC漏油配置总结

    为什么说JAVA中要慎重使用继承   这篇文章的主题并非鼓励不使用继承,而是仅从使用继承带来的问题出发,讨论继承机制不太好的地方,从而在使用时慎重选择,避开可能遇到的坑. JAVA中使用到继承就会有两 ...

  2. Openmp Runtime 库函数汇总(下)——深入剖析锁🔒原理与实现

    Openmp Runtime 库函数汇总(下)--深入剖析锁原理与实现 前言 在本篇文章当中主要给大家介绍一下 OpenMP 当中经常使用到的锁并且仔细分析它其中的内部原理!在 OpenMP 当中主要 ...

  3. 如何检测被锁住的Oracle存储过程及处理办法汇总(转)

    1.查看是哪一个存储过程被锁住查V$DB_OBJECT_CACHE视图select * from V$DB_OBJECT_CACHE where owner='过程的所属用户' AND LOCKS!= ...

  4. java多线程知识汇总(三)如何选择锁?如何加锁

    1.锁,保证的是被锁的代码,一次执行完毕才能被其他线程执行,锁保证了一个线程执行过程中不被其他线程打断.以保证数据的准确性. 2.数据的读写过程,是有冲突的,当一个线程正在读数据,另一个线程正在写同一 ...

  5. mysql中的锁表语句查看方法汇总

    mysql> show status like 'Table%'; +----------------------------+----------+ | Variable_name | Val ...

  6. Oracle手边常用70则脚本知识汇总

    Oracle手边常用70则脚本知识汇总 作者:白宁超 时间:2016年3月4日13:58:36 摘要: 日常使用oracle数据库过程中,常用脚本命令莫不是用户和密码.表空间.多表联合.执行语句等常规 ...

  7. BW知识问答汇总

    什么是sap的星型结构,能不能详细讲解一下? Cube的星型结构中SID技术的优点有哪些? 什么是BW的星型结构,与传统的星型结构的区别是什么? SAP的星型结构相对于传统的星型结构优势? Cube与 ...

  8. Linux 2.6内核中新的锁机制--RCU

    转自:http://www.ibm.com/developerworks/cn/linux/l-rcu/ 一. 引言 众所周知,为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁 ...

  9. Mac Pro 入门、遇到的问题、个性化设置 汇总

    入门资料 入门一:Mac 基本用法 入门二:Mac 使用VMware Fusion虚拟机 入门三:Mac 使用brew安装软件 问题汇总 [问题1]如何复制文本? 一只手指头按下,另外一只手指头滑动选 ...

  10. GitHub上史上最全的Android开源项目分类汇总 (转)

    GitHub上史上最全的Android开源项目分类汇总 标签: github android 开源 | 发表时间:2014-11-23 23:00 | 作者:u013149325 分享到: 出处:ht ...

随机推荐

  1. 《Python深度学习》《卷积神经网络的可视化》精读

    对于大多数深度学习模型,模型学到的表示都难以用人类可以理解的方式提取和呈现.但对于卷积神经网络来说,我们可以很容易第提取模型学习到的表示形式,并以此加深对卷积神经网络模型运作原理的理解. 这篇文章的内 ...

  2. 【TensorFlow】Tensorflow-GPU 环境搭建教程(附 Windows 版本对应表及 CUDA GPU 计算能力表)

    conda教程(推荐):『Tensorflow GPU Installation Made Easy: Use conda instead of pip [Update-2] | by Harveen ...

  3. centos 服务器配置网络ifconfig位置

    path :   /etc/sysconfig/network-scripts/ifcfg-enoxxx

  4. 修改Linux终端默认编辑器,实现代码高亮

    echo export EDITOR=vim >>/etc/profile.d/env.sh

  5. go interface nil 比较

    package main import "fmt" type MyselfError struct{} func (m *MyselfError) Error() string { ...

  6. 3阶(次)贝塞尔曲线的JavaScript(JS)实现

    php中文网数学符号的显示太差了,推荐看这里 贝塞尔曲线简介:贝塞尔曲线,是贝塞尔老爷子在使用电子计算机设计汽车零件的时候 进行曲面设计而采用的一种参数化的样条曲线. 一般参数方程: B(t) = \ ...

  7. Mac实现截图OCR输出文字功能

    为什么要这个功能 工作中会经常遇到需要识别一个图片中的文本内容,需要快速的OCR识别,从而提高工作效率: 如何实现 snipaste + pngpaste + tesseract 安装流程与使用 安装 ...

  8. RayLink远程控制软件:叮~你收到一份年度关键词报告

    叮~~~ 今天是12月31日,2022年的最后一天.今天过后,明天就是2023年啦!R君提前恭祝大家新年快乐,温情满满的跨年之际,RayLink感恩2022遇见大家,2023还请大家多多关照~ 202 ...

  9. .Net Standard .Net Framework .Net Core 版本对应

  10. 多个el-table在使用v-if在同一页面切换渲染时相互影响的解决办法

    解决办法 给每个el-table设置一个唯一的key值,如: <el-table key='uniqueName' ></el-table> <el-table key= ...