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的更多相关文章

  1. Python多线程锁

    [Python之旅]第六篇(四):Python多线程锁   python lock 多线程 多线程使用方法 多线程锁 摘要:   在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来 ...

  2. java 并发多线程 锁的分类概念介绍 多线程下篇(二)

    接下来对锁的概念再次进行深入的介绍 之前反复的提到锁,通常的理解就是,锁---互斥---同步---阻塞 其实这是常用的独占锁(排它锁)的概念,也是一种简单粗暴的解决方案 抗战电影中,经常出现为了阻止日 ...

  3. Java多线程--锁的优化

    Java多线程--锁的优化 提高锁的性能 减少锁的持有时间 一个线程如果持有锁太长时间,其他线程就必须等待相应的时间,如果有多个线程都在等待该资源,整体性能必然下降.所有有必要减少单个线程持有锁的时间 ...

  4. synchronized与static synchronized 的差别、synchronized在JVM底层的实现原理及Java多线程锁理解

    本Blog分为例如以下部分: 第一部分:synchronized与static synchronized 的差别 第二部分:JVM底层又是怎样实现synchronized的 第三部分:Java多线程锁 ...

  5. JUC之多线程锁问题

    多线程锁 8种问题锁状态: 该部分全部围绕的是以下内容并结合相应的例子:synchronized实现同步的基础:Java中每个对象都可以作为锁. 具体表现为以下三种形式:(之前只是简单的了解) 对于普 ...

  6. 多线程锁--怎么理解Condition

    在java.util.concurrent包中,有两个很特殊的工具类,Condition和ReentrantLock,使用过的人都知道,ReentrantLock(重入锁)是jdk的concurren ...

  7. Java 多线程 锁 存款 取款

    http://jameswxx.iteye.com/blog/806968 最近想将java基础的一些东西都整理整理,写下来,这是对知识的总结,也是一种乐趣.已经拟好了提纲,大概分为这几个主题: ja ...

  8. 并发数据(锁)ReaderWriterLockSlim

    ReaderWriterLockSlim 类 ReaderWriterLockSlim 类支持三种锁定模式:Read,Write,UpgradeableRead.这三种模式对应的方法分别是 Enter ...

  9. [java多线程] - 锁机制&同步代码块&信号量

    在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同 ...

随机推荐

  1. 第一个VS2015 Xaramin Android项目(终)

    其实还有一个问题没解决,也拖很久了.中途公司的项目太紧导致无法学习更新. 之前的问题是这样的:项目搭建成功了,App也成功发布到虚拟机.便尝试增加控件 Xaml 设计界面如下: 但是在虚拟机运行却这样 ...

  2. Chapter6_访问权限控制_访问权限修饰词

    Java中有四种访问权限,public,private,protected和包访问权限,它们是置于类中每一个成员之前的定义,无论是一个域还是一个方法,下面一一介绍. 一.包访问权限 如果不提供任何访问 ...

  3. Startls Back 引起的 win10升级之后的闪屏问题

    win10 更新之后出现闪频问题. 有人说是和startls back 有关,需要卸载startls back, 但是进入安全模式下显示此 程序无法打开,无法卸载. 后来看到有人更新到startls ...

  4. 将地图定位封装为ng指令

    一.HTML结构<div tabindex="-1" class="modal fade in active modal-map" role=" ...

  5. 20175316盛茂淞 2018-2019-2 《Java程序设计》第8周学习总结

    20175316盛茂淞 2018-2019-2 <Java程序设计>第8周学习总结 教材学习内容总结 第十五章 泛型 -- 主要目的是可以建立具有类型安全的集合框架,如链表.散列映射等数据 ...

  6. pt-query-digest 使用说明

    pt-query-digest --user=anemometer --password=123456 --socket=/tmp/mysql.sock --port=43306 --review h ...

  7. [swarthmore cs75] Compiler 6 – Fer-de-lance

    课程回顾 Swarthmore学院16年开的编译系统课,总共10次大作业.本随笔记录了相关的课堂笔记以及第8次大作业. First-class function: It treats function ...

  8. python项目1:自动解压并删除压缩包

    目的:实现压缩包的自动解压及删除. 思路:获取压缩包 > 解压 > 删除压缩包 代码实现:此处代码实现前提为.py文件和压缩包在同一文件夹 # 导入需要的包 import os impor ...

  9. python装饰器同时支持有参数和无参数的练习题

    ''' 预备知识: …… @decorator def f(*args,**kwargs): pass # 此处@decorator  等价于 f = decorator(f) @decorator2 ...

  10. RSA 前段加密 java 后台解密 已调试通过

    本人整理网上的.好多网上的调不通.在这里把调试好的贴出来. 1.   异步获取公钥(后台获取):你也可以将公钥串写在页面上: var publicKey = null; $.ajax({ url: c ...