一、前言

  本文章汇总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. centos7.2下配置DNS服务器

    https://baijiahao.baidu.com/s?id=1748980460185046641&wfr=spider&for=pc 1.安装bind(服务器) yum -y ...

  2. centos 绑定多ip

    复制ifcfg-ens192 文件,为 ifcfg-ens192:0 修改ip 和 device 为 "ens192:0",其他不变,service network restart ...

  3. VS Code:4个中文乱码问题及解决方法-转载

    https://www.jianshu.com/p/6a2c21cc07bb   1. 背景   凡是编程软件,特别是国外的软件,都有或多或少的中文乱码问题(毕竟程序都是用英文写的).现提出VS Co ...

  4. <<运算?&=、|=、 ^=、<<=、>>=的意思? 十六进制前缀是 0x。

    <<运算? a<<b 表示把a转为二进制后左移b位(在后面添加 b个0).例如100的二进制表示为1100100,100左移2位后(后面加2个零):1100100<< ...

  5. C语言学习--常量指针与指针常量

    指针常量 #include<stdio.h> #include<string.h> //常量指针:是一个指针, 定义不用初始化, 能改变指向,但是指向的内容不能被修改 cons ...

  6. RTT笔记-分析自动初始化机制转

    首先全局搜索一个任意的自启动宏,便能找到在rtdef.h中由如下定义 1 #define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1") 2 ...

  7. CF1786E题解

    容易为本题的弱化版CF1786C想出一个贪心: #include<bits/stdc++.h> using namespace std; #define int long long int ...

  8. C#封装以及访问修饰符

    封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中".在面向对象程序设计方法论中,封装是为了防止对实现细节的访问. 抽象和封装是面向对象程序设计的相关特性.抽象允许相关 ...

  9. window批处理一键打开多个exe

    使用批处理的start命令,格式为start /d "绝对路径" 目标exe名,记得路径和exe名间有个空格 @echo off start /d "E:\demo\&q ...

  10. Web Uploader上传文件

    Web Uploader是百度提供的. 1:下载:http://fex.baidu.com/webuploader/(官方下载/示例) 2:使用Web Uploader文件上传需要引入三种资源:JS, ...