C# 多线程锁之ReaderWriterLockSlim
1、简介
.NET 3.5 开始 ReaderWriterLockSlim登上舞台,ReaderWriterLockSlim 可以看做是 ReaderWriterLock 的升级版。 由于 ReaderWriterLockSlim 默认不支持递归调用、所以在某种意义上来说更不容易造成死锁。
ReaderWriterLockSlim 类支持三种锁定模式:Read,Write,UpgradeableRead。这三种模式对应的方法分别是 EnterReadLock,EnterWriteLock,EnterUpgradeableReadLock 。再就是与此对应的 TryEnterReadLock,TryEnterWriteLock,TryEnterUpgradeableReadLock,ExitReadLock,ExitWriteLock,ExitUpgradeableReadLock。Read 和 Writer 锁定模式比较简单易懂:Read 模式是典型的共享锁定模式,任意数量的线程都可以在该模式下同时获得锁;Writer 模式则是互斥模式,在该模式下只允许一个线程进入该锁。UpgradeableRead 锁定模式可能对于大多数人来说比较新鲜,但是在数据库领域却众所周知。
1、对于同一把锁、多个线程可同时进入读模式。
2、对于同一把锁、同时只允许一个线程进入写模式。
3、对于同一把锁、同时只允许一个线程进入可升级的读模式。
4、通过默认构造函数创建的读写锁是不支持递归的,若想支持递归 可通过构造 ReaderWriterLockSlim(LockRecursionPolicy) 创建实例。
5、对于同一把锁、同一线程不可两次进入同一锁状态(开启递归后可以)
6、对于同一把锁、即便开启了递归、也不可以在进入读模式后再次进入写模式或者可升级的读模式(在这之前必须退出读模式)。
7、再次强调、不建议启用递归。
8、读写锁具有线程关联性,即两个线程间拥有的锁的状态相互独立不受影响、并且不能相互修改其锁的状态。
9、升级状态:在进入可升级的读模式 EnterUpgradeableReadLock后,可在恰当时间点通过EnterWriteLock进入写模式。
10、降级状态:可升级的读模式可以降级为读模式:即在进入可升级的读模式EnterUpgradeableReadLock后, 通过首先调用读取模式EnterReadLock方法,然后再调用 ExitUpgradeableReadLock 方法。
这段简介来自https://www.cnblogs.com/majiang/p/8133979.html,来自一个前辈的文章,总结的很好,而且有源码解析,有兴趣的可以观看,通过这段话结合MSDN关于ReaderWriterLockSlim的介绍,能大致得知道ReaderWriterLockSlim得用处,在多线程并发操作共享资源时,很有用处.
2、通过ReaderWriterLockSlim封装一个同步缓存实例
下面时MS提供的封装,我做了略微的修改,添加了一些注释,使API更能看懂,代码如下:
public class SynchronizedCache
{
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(); /// <summary>
/// 同步缓存块维护的数据资源
/// </summary>
private Dictionary<int, string> innerCache = new Dictionary<int, string>(); /// <summary>
/// 同步缓存块维护的数据资源长度
/// </summary>
public int Count
{
get { return innerCache.Count; }
} /// <summary>
/// 线程安全的添加操作
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void Add(int key,string value)
{
//尝试进入写入模式锁定状态
cacheLock.EnterWriteLock();
try
{
innerCache.Add(key, value);
}
finally
{
//退出写入模式锁定状态
cacheLock.ExitWriteLock();
}
} /// <summary>
/// 带锁超时的添加的操作
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="timeout"></param>
/// <returns></returns>
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;
}
} /// <summary>
/// 线程安全的读取操作
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string Read(int key)
{
cacheLock.EnterReadLock();
try
{
return innerCache[key];
}
finally
{
cacheLock.ExitReadLock();
}
} /// <summary>
/// 线程安全的添加修改操作
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
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();
}
} /// <summary>
/// 线程安全的删除操作
/// </summary>
/// <param name="key"></param>
public void Delete(int key)
{
cacheLock.EnterWriteLock();
try
{
innerCache.Remove(key);
}
finally
{
cacheLock.ExitWriteLock();
}
} /// <summary>
/// 添加或修改时产生的状态
/// </summary>
public enum AddOrUpdateStatus
{
Added,
Updated,
Unchanged
}; /// <summary>
/// 析构 释放资源
/// </summary>
~SynchronizedCache()
{
if (cacheLock != null) cacheLock.Dispose();
}
}
下面时使用案列代码如下:
var lockCache = new SynchronizedCache();
var tasks = new List<Task>();//模拟线程集合 //注入写入内容线程
tasks.Add(Task.Run(() => {
var list = new List<string> {"钟","声","响","起","归","家","的","讯","号"};
var listCount = list.Count;
for (var i = ; i < listCount; i++)
{
lockCache.Add(i, list[i]);
}
Console.WriteLine($"Task {Task.CurrentId} wrote {listCount} items\n");
})); //注入两个读线程,一个正向遍历同步缓存块维护的数据资源一个逆向遍历同步缓存块维护的数据资源
//由于读线程可能在写线程之前执行,所以输入内容时可能为空
for (var i = ; i <= ; i++)
{
var flag = Convert.ToBoolean(i);
tasks.Add(Task.Run(() =>
{
int startIndex, lastIndex, step;//开始、结束索引、递增指数
string outPut=string.Empty;//输出
int items;//线程执行顺序可能不同,所以个参数用于判断在执行读取操作时,上面的写入线程是否执行完毕
do
{
items = lockCache.Count;
//正向遍历
if (!flag)
{
startIndex = ;
lastIndex = items;
step = ;
}
//反向遍历
else
{
startIndex = items - ;
lastIndex = ;
step = -;
}
for (var j = startIndex; flag ? j >= lastIndex : j < lastIndex; j += step)
{
outPut += $"{lockCache.Read(j)} ";
}
Console.WriteLine($"Task {Task.CurrentId} read {items} items: {outPut}\n");
} while (lockCache.Count == | items< lockCache.Count);
}));
} //注入一个线程去修改数据
tasks.Add(Task.Run(() => {
Thread.Sleep();//强制当前线程休息,防止写入数据线程还没有执行完毕,就去更新了数据
for (int ctr =; ctr < lockCache.Count; ctr++)
{
string value = lockCache.Read(ctr);
if (value == "家")
if (lockCache.AddOrUpdate(ctr, "Home") != SynchronizedCache.AddOrUpdateStatus.Unchanged)
Console.WriteLine("Changed '家' to 'Home'");
}
})); Task.WhenAll(tasks).ContinueWith(task =>
{
Console.WriteLine();
Console.WriteLine("Values in synchronized cache: ");
for (int ctr = ; ctr < lockCache.Count; ctr++)
Console.WriteLine(" {0}: {1}", ctr, lockCache.Read(ctr));
}); Console.ReadKey();

调用完毕,有点ConncurrentDictionary的味道,还没看它的代码,接下去的随笔会分析,对比下两种方式的差距.
C# 多线程锁之ReaderWriterLockSlim的更多相关文章
- Python多线程锁
[Python之旅]第六篇(四):Python多线程锁 python lock 多线程 多线程使用方法 多线程锁 摘要: 在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来 ...
- java 并发多线程 锁的分类概念介绍 多线程下篇(二)
接下来对锁的概念再次进行深入的介绍 之前反复的提到锁,通常的理解就是,锁---互斥---同步---阻塞 其实这是常用的独占锁(排它锁)的概念,也是一种简单粗暴的解决方案 抗战电影中,经常出现为了阻止日 ...
- Java多线程--锁的优化
Java多线程--锁的优化 提高锁的性能 减少锁的持有时间 一个线程如果持有锁太长时间,其他线程就必须等待相应的时间,如果有多个线程都在等待该资源,整体性能必然下降.所有有必要减少单个线程持有锁的时间 ...
- synchronized与static synchronized 的差别、synchronized在JVM底层的实现原理及Java多线程锁理解
本Blog分为例如以下部分: 第一部分:synchronized与static synchronized 的差别 第二部分:JVM底层又是怎样实现synchronized的 第三部分:Java多线程锁 ...
- JUC之多线程锁问题
多线程锁 8种问题锁状态: 该部分全部围绕的是以下内容并结合相应的例子:synchronized实现同步的基础:Java中每个对象都可以作为锁. 具体表现为以下三种形式:(之前只是简单的了解) 对于普 ...
- 多线程锁--怎么理解Condition
在java.util.concurrent包中,有两个很特殊的工具类,Condition和ReentrantLock,使用过的人都知道,ReentrantLock(重入锁)是jdk的concurren ...
- Java 多线程 锁 存款 取款
http://jameswxx.iteye.com/blog/806968 最近想将java基础的一些东西都整理整理,写下来,这是对知识的总结,也是一种乐趣.已经拟好了提纲,大概分为这几个主题: ja ...
- 并发数据(锁)ReaderWriterLockSlim
ReaderWriterLockSlim 类 ReaderWriterLockSlim 类支持三种锁定模式:Read,Write,UpgradeableRead.这三种模式对应的方法分别是 Enter ...
- [java多线程] - 锁机制&同步代码块&信号量
在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同 ...
随机推荐
- Python 3.6安装yaml时报"AttributeError: module 'pip' has no attribute 'main'"和“Non-zero exit code”错误
1.Python 3.6安装yaml时一开始报AttributeError: module 'pip' has no attribute错误,根据网上提供的解决方法修改Pycharm安装目录D:\Pr ...
- tensorflow 源码编译
https://blog.csdn.net/xsfl1234/article/details/67669707 https://blog.csdn.net/guxi123/article/detail ...
- Linux编程基础——GDB(设置断点)
启动GDB后,首先就是要设置断点,程序中断后才能调试.在gdb中,断点通常有三种形式: 断点(BreakPoint): 在代码的指定位置中断,这个是我们用得最多的一种.设置断点的命令是break,它通 ...
- 2019.03.26 bzoj4448: [Scoi2015]情报传递(归并排序+树链剖分)
传送门 题意简述: 给一棵nnn个点的树,树上每个点表示一个情报员,一共有mmm天,每天会派发以下两种任务中的一个任务: 1.搜集情报:指派T号情报员搜集情报 2.传递情报:将一条情报从X号情报员传递 ...
- [小结]InnoDB体系结构及工作原理
参阅:<innodb存储引擎内幕>原创文章,会不定时更新,转发请标明出处:http://www.cnblogs.com/janehoo/p/7717041.html 一.概述: innod ...
- java跨域问题
public class SimpleCORSFilter implements Filter{ @Override public void destroy() { } @Override publi ...
- Kalman Filters
|—定位—|—蒙特卡洛方法(定位自身) | |—卡尔曼滤波器(定位其他车辆) |—高斯函数 |—循环两个过程—|—测量(测量更新) | ...
- Self referencing loop detected for property 错误
EF 序列化返回json时 报错:Self referencing loop detected for property 解决方案:在webapiconfig.cs文件中,增加设置: 1.config ...
- unigui+fastreport报表打印
unigui+fastreport报表打印 unigui+fastreport报表打印 FASTREPORT导出成PDF报表,UNIGUI的TUniURLFrame显示PDF报表并打印. func ...
- python模块的导入的两种方式区别详解
Python 有两种导入模块的方法.两种都有用,你应该知道什么时候使用哪一种方法.一种方法,import module,另一种是from module import,下面是 from module i ...