C# 锁汇总
一、前言
本文章汇总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# 锁汇总的更多相关文章
- 为什么说JAVA中要慎重使用继承 C# 语言历史版本特性(C# 1.0到C# 8.0汇总) SQL Server事务 事务日志 SQL Server 锁详解 软件架构之 23种设计模式 Oracle与Sqlserver:Order by NULL值介绍 asp.net MVC漏油配置总结
为什么说JAVA中要慎重使用继承 这篇文章的主题并非鼓励不使用继承,而是仅从使用继承带来的问题出发,讨论继承机制不太好的地方,从而在使用时慎重选择,避开可能遇到的坑. JAVA中使用到继承就会有两 ...
- Openmp Runtime 库函数汇总(下)——深入剖析锁🔒原理与实现
Openmp Runtime 库函数汇总(下)--深入剖析锁原理与实现 前言 在本篇文章当中主要给大家介绍一下 OpenMP 当中经常使用到的锁并且仔细分析它其中的内部原理!在 OpenMP 当中主要 ...
- 如何检测被锁住的Oracle存储过程及处理办法汇总(转)
1.查看是哪一个存储过程被锁住查V$DB_OBJECT_CACHE视图select * from V$DB_OBJECT_CACHE where owner='过程的所属用户' AND LOCKS!= ...
- java多线程知识汇总(三)如何选择锁?如何加锁
1.锁,保证的是被锁的代码,一次执行完毕才能被其他线程执行,锁保证了一个线程执行过程中不被其他线程打断.以保证数据的准确性. 2.数据的读写过程,是有冲突的,当一个线程正在读数据,另一个线程正在写同一 ...
- mysql中的锁表语句查看方法汇总
mysql> show status like 'Table%'; +----------------------------+----------+ | Variable_name | Val ...
- Oracle手边常用70则脚本知识汇总
Oracle手边常用70则脚本知识汇总 作者:白宁超 时间:2016年3月4日13:58:36 摘要: 日常使用oracle数据库过程中,常用脚本命令莫不是用户和密码.表空间.多表联合.执行语句等常规 ...
- BW知识问答汇总
什么是sap的星型结构,能不能详细讲解一下? Cube的星型结构中SID技术的优点有哪些? 什么是BW的星型结构,与传统的星型结构的区别是什么? SAP的星型结构相对于传统的星型结构优势? Cube与 ...
- Linux 2.6内核中新的锁机制--RCU
转自:http://www.ibm.com/developerworks/cn/linux/l-rcu/ 一. 引言 众所周知,为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁 ...
- Mac Pro 入门、遇到的问题、个性化设置 汇总
入门资料 入门一:Mac 基本用法 入门二:Mac 使用VMware Fusion虚拟机 入门三:Mac 使用brew安装软件 问题汇总 [问题1]如何复制文本? 一只手指头按下,另外一只手指头滑动选 ...
- GitHub上史上最全的Android开源项目分类汇总 (转)
GitHub上史上最全的Android开源项目分类汇总 标签: github android 开源 | 发表时间:2014-11-23 23:00 | 作者:u013149325 分享到: 出处:ht ...
随机推荐
- 自考网络原理:安全套接字层SSL
对ssl/tls的理解 前:SSL; 后:TLS: 以下是B站上的up主讲的,非常的深入浅出,讲的很好.感谢技术蛋老师. https://www.bilibili.com/video/BV1KY411 ...
- vi 快捷键/ctags
vi 配置 syntax enableset nu set relativenumberset hlsearch set autoindentset shiftwidth=4set tabstop=4 ...
- https原理(七)其他
1 fiddler原理+fiddler为什么抓chrome而不能抓curl和httpclient?fiddler为什么能篡改报文? 中的ssl pinning 本质即是双向ssl https://zh ...
- dart extends 覆盖规则
1,不覆写super的变量,child会自动继承super的变量.即使是在child里给super赋值,child里也是可以访问到的,可能是因为引用的关系. 2,只要覆写了super的变量,只给sup ...
- div 自动高度
问题: 一个页面分上下二部分,上部分高度是固定不变的,要求下部分高度自动占满屏幕,如何实现? 第一感觉是准备用 JS 实现. 今天发现一个用 CSS 实现的方法 position: fixed; bo ...
- ubuntu添加新硬盘进行分区,并挂载到/home
ubuntu添加新硬盘,进行分区,并挂载到/home目录. 1.查看已有的磁盘,可以看到sdb还没有分区. > sudo fdisk -l 可以看到各个硬盘的设备名,一般以sda.sdb.sdc ...
- nginx 编译安装支持 upload progress module, upload module
下载地址: wiki.nginx.org/HttpUploadModule wiki.nginx.org/HttpUploadProgressModule 下载后解压至:/usr/src/文件夹下,其 ...
- Qt开发环境的建立
在建立开发环境之前,先来了解一下Qt是什么以及它的历史变迁.Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架.它既可以开发GUI程序,也可用于开发非GUI程序, ...
- MySQL索引 及 EXPLAIN
1.索引长度计算: 1.索引字段,没有设置NOT NULL,则需要加一个字节. 2.定长字段:tinyiny占1个字节.int占4个字节.bitint占8个字节.date占3个字节.datetime占 ...
- js已知多边形坐标点,求多边形的中心点坐标
你需要传入的数据类型如下: // 取面对象 中心点 var calculateCenter = function (lnglatarr) { var total = lnglatarr.length; ...