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. c语言基础课第一次作业

    1)大学和高中最大的不同是没有人天天看着你,请看大学理想的师生关系是?有何感想? 通过阅读邹欣老师的博客,了解到了老师心中理想的师生关系是(健身教练与健身学员).在初中,高中我们一直都是填鸭式教育,像 ...

  2. 防止用户重发发生ajax请求

    1.前端限制 点击提交后,将该元素禁用,等待请求结束后再次释放(解除禁用). 可以使用ajax中的 success 请求成功后的回调函数进行按钮释放. 2.防抖动 暴力连续点击按钮,可以通过闭包里的  ...

  3. h3c acl配置一列

    acl number 3004 rule 0 permit ip source 10.2.1.4 0 rule 1 deny ip source 192.168.1.91 0 rule 2 deny ...

  4. Buffer.h

    #ifndef __NOXIMBUFFER_H__ #define __NOXIMBUFFER_H__ #include <cassert> #include <queue> ...

  5. Unity打包提示UnityEditor.BuildPlayerWindow+BuildMethodException: Build failed with errors.错误

    不要将打包的输出路径设置为Assets文件夹下面即可,MD真坑 老外给出的解释: As you have noticed after you click build settings you are ...

  6. 更改MAC地址,突破公司绑定MAC地址的限制

    步骤/方法 1 打开开始菜单,选择控制面板. 2   3 打开控制面板项,选择网络和共享中心. 4   5 选择更改适配器设置. 6   7 选择本地要修改MAC地址的网卡. 8   9 右键该网卡, ...

  7. Strusts2

    Strusts2的原理很简单,首先,地址栏输入http://xxxx/xxxx/webapp/xx.action首先,请求会通过httpservlet发送给tomcat容器,tomcat发现这个请求是 ...

  8. ext__给grid Panel设置绑定事件

    使用面板来展示详情信息 1.创建一个面板 (双击添加) 2.给该面板设置itemid的值为:detailPanel 3.给面板设置模板 4.添加下面的内容 id:{id}</br> nam ...

  9. python图形界面编程

    EasyGui(easygui-docs-0.96\tutorial\index.html) import easygui as g import sys while 1: g.msgbox('mes ...

  10. program--历史故事