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. Redis学习笔记:与SpringBoot结合使用

    首先需要在pom文件中导入相应的Redis依赖(版本可以会变化,下面坐标也可能会变化) <dependency> <groupId>org.springframework.bo ...

  2. 返回上一页 html A标记代码

    <a class="sjad" href="#" onClick="javascript:history.back(-1);"> ...

  3. 运用SharedPreferences“偷取”输入的信息

    运用SharedPreferences"偷取"输入的信息 本次的任务是 利用SharedPreferences来完成信息的保存和读取 就是你输入什么 手机就可以把输入的内容&quo ...

  4. nginx常用服务配置

    一.nginx.conf的配置方式,创建新vhost user nginx; worker_processes ; worker_cpu_affinity ; worker_rlimit_nofile ...

  5. 每日一练ACM 2019.0417

    Problem Description 给定两个正整数,计算这两个数的最小公倍数.   Input 输入包含多组测试数据,每组只有一行,包括两个不大于1000的正整数.   Output 对于每个测试 ...

  6. NIOS II 之串口学习

    UART中有6个寄存器分别为control, status, rxdata, txdata, divisor,endofpacket. 的寄存器是16位位宽的. UART会产生一个高电平的中断,当接收 ...

  7. Android Studio将引用第三方jar包的library打包成jar包

    在该module的build.gradle中添加 task makeJar(type: Jar) { archiveName 'mysdk.jar' from('build/intermediates ...

  8. C++ Error C2664:无法将参数 1 从“const char [9]”转换为“LPCWSTR”解决方案

    问题出现 编译平台:VS2013     Windows 出现地方:在使用LoadLibrary( )函数动态链接DLL文件时出现的一个问题 Eg.   在使用 UNICODE字符的工程中,  HIN ...

  9. P层

    package net.goeasyway.uploadimage.presenter; import net.goeasyway.uploadimage.model.Photo;import net ...

  10. 【repost】javascript callback

    在javascript中回调函数非常重要,它们几乎无处不在.像其他更加传统的编程语言都有回调函数概念,但是非常奇怪的是,完完整整谈论回调函数的在线教程比较少,倒是有一堆关于call()和apply() ...