.NET 同步与异步之锁(ReaderWriterLockSlim)(八)
本随笔续接:.NET 同步与异步之锁(Lock、Monitor)(七)
由于锁 ( lock 和 Monitor ) 是线程独占式访问的,所以其对性能的影响还是蛮大的,那有没有一种方式可是实现:允许多个线程同时读数据、只允许一个线程写数据呢?答案是肯定的。
读写锁 ReaderWriterLock 、就是 支持单个写线程和多个读线程的锁。自.NET 3.5 开始 ReaderWriterLockSlim、登上舞台,ReaderWriterLockSlim 可以看做是 ReaderWriterLock 的升级版。 由于 ReaderWriterLockSlim 默认不支持递归调用、所以在某种意义上来说更不容易造成死锁。
一、先看一下demo(来源msdn代码示例):
- 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();
- }
- }
- private void ReaderWriterLock()
- {
- var sc = new SynchronizedCache();
- var tasks = new List<Task>();
- int itemsWritten = ;
- // Execute a writer.
- tasks.Add(Task.Run(() =>
- {
- String[] vegetables = { "broccoli", "cauliflower",
- "carrot", "sorrel", "baby turnip",
- "beet", "brussel sprout",
- "cabbage", "plantain",
- "spinach", "grape leaves",
- "lime leaves", "corn",
- "radish", "cucumber",
- "raddichio", "lima beans" };
- for (int ctr = ; ctr <= vegetables.Length; ctr++)
- sc.Add(ctr, vegetables[ctr - ]);
- itemsWritten = vegetables.Length;
- base.PrintInfo(string.Format("Task {0} wrote {1} items\n", Task.CurrentId, itemsWritten));
- }));
- // Execute two readers, one to read from first to last and the second from last to first.
- for (int ctr = ; ctr <= ; ctr++)
- {
- bool desc = Convert.ToBoolean(ctr);
- tasks.Add(Task.Run(() =>
- {
- int start, last, step;
- int items;
- do
- {
- String output = String.Empty;
- items = sc.Count;
- if (!desc)
- {
- start = ;
- step = ;
- last = items;
- }
- else
- {
- start = items;
- step = -;
- last = ;
- }
- for (int index = start; desc ? index >= last : index <= last; index += step)
- output += String.Format("[{0}] ", sc.Read(index));
- base.PrintInfo(string.Format("Task {0} read {1} items: {2}\n", Task.CurrentId, items, output));
- } while (items < itemsWritten | itemsWritten == );
- }));
- }
- // Execute a red/update task.
- tasks.Add(Task.Run(() =>
- {
- Thread.Sleep();
- for (int ctr = ; ctr <= sc.Count; ctr++)
- {
- String value = sc.Read(ctr);
- if (value == "cucumber")
- if (sc.AddOrUpdate(ctr, "green bean") != SynchronizedCache.AddOrUpdateStatus.Unchanged)
- base.PrintInfo("Changed 'cucumber' to 'green bean'");
- }
- }));
- // Wait for all three tasks to complete.
- Task.WaitAll(tasks.ToArray());
- // Display the final contents of the cache.
- base.PrintInfo("");
- base.PrintInfo("Values in synchronized cache: ");
- for (int ctr = ; ctr <= sc.Count; ctr++)
- base.PrintInfo(string.Format(" {0}: {1}", ctr, sc.Read(ctr)));
- }
Demo
二、通过Demo我们来看一下 ReaderWriterLockSlim 的用法:
1、EnterWriteLock 进入写模式锁定状态
2、EnterReadLock 进入读模式锁定状态
3、EnterUpgradeableReadLock 进入可升级的读模式锁定状态
并且三种锁定模式都有超时机制、对应 Try... 方法,退出相应的模式则使用 Exit... 方法,而且所有的方法都必须是成对出现的。
三、备注及注意事项
1、对于同一把锁、多个线程可同时进入 读模式。
2、对于同一把锁、同时只允许一个线程进入 写模式。
3、对于同一把锁、同时只允许一个线程进入 可升级的读模式。
4、通过默认构造函数创建的读写锁是不支持递归的,若想支持递归 可通过构造 ReaderWriterLockSlim(LockRecursionPolicy) 创建实例。
5、对于同一把锁、同一线程不可两次进入同一锁状态(开启递归后可以)
6、对于同一把锁、即便开启了递归、也不可以在进入读模式后再次进入写模式或者可升级的读模式(在这之前必须退出读模式)。
7、再次强调、不建议启用递归。
8、读写锁具有线程关联性,即 两个线程间拥有的锁的状态 相互独立不受影响、并且不能相互修改其锁的状态。
9、升级状态:在进入可升级的读模式 EnterUpgradeableReadLock 后,可在恰当时间点 通过 EnterWriteLock 进入写模式。
10、降级状态:可升级的读模式可以降级为读模式:即 在进入可升级的读模式 EnterUpgradeableReadLock 后, 通过首先调用读取模式 EnterReadLock 方法,然后再调用 ExitUpgradeableReadLock 方法。
随笔暂告一段落、下一篇随笔介绍:轻量级的锁(Interlocked、SpinLock)(预计1篇随笔)
附,Demo : http://files.cnblogs.com/files/08shiyan/ParallelDemo.zip
参见更多:随笔导读:同步与异步
(未完待续...)
.NET 同步与异步之锁(ReaderWriterLockSlim)(八)的更多相关文章
- .NET 同步与异步之锁(Lock、Monitor)(七)
本随笔续接:.NET同步与异步之相关背景知识(六) 在上一篇随笔中已经提到.解决竞争条件的典型方式就是加锁 ,那本篇随笔就重点来说一说.NET提供的最常用的锁 lock关键字 和 Monitor. 一 ...
- .NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)
本随笔续接:.NET 同步与异步之锁(ReaderWriterLockSlim)(八) 之前的随笔已经说过.加锁虽然能很好的解决竞争条件,但也带来了负面影响:性能方面的负面影响.那有没有更好的解决方案 ...
- CIL锁,GIL与线程池的区别,进程池和线程池,同步与异步
一.GIL锁 什么是GIL? 全局解释器锁,是加在解释器上的互斥锁 GC是python自带的内存管理机制,GC的工作原理:python中的内存管理使用的是应用计数,每个数会被加上一个整型的计数器,表示 ...
- [多线程]线程基础(对象锁、class锁、同步、异步)
synchronized.volatile.ReentrantLock.concurrent 线程安全:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法) ...
- .NET同步与异步之相关背景知识(六)
在之前的五篇随笔中,已经介绍了.NET 类库中实现并行的常见方式及其基本用法,当然.这些基本用法远远不能覆盖所有,也只能作为一个引子出现在这里.以下是前五篇随笔的目录: .NET 同步与异步之封装成T ...
- input屏蔽历史记录 ;function($,undefined) 前面的分号是什么用处 JSON 和 JSONP 两兄弟 document.body.scrollTop与document.documentElement.scrollTop兼容 URL中的# 网站性能优化 前端必知的ajax 简单理解同步与异步 那些年,我们被耍过的bug——has
input屏蔽历史记录 设置input的扩展属性autocomplete 为off即可 ;function($,undefined) 前面的分号是什么用处 ;(function($){$.ex ...
- 半同步半异步模式的实现 - MSMQ实现
半同步半异步模式的实现 - MSMQ实现 所谓半同步半异步是指,在某个方法调用中,有些代码行是同步执行方式,有些代码行是异步执行方式,下面我们来举个例子,还是以经典的PlaceOrder来说,哈哈. ...
- python并发编程(并发与并行,同步和异步,阻塞与非阻塞)
最近在学python的网络编程,学了socket通信,并利用socket实现了一个具有用户验证功能,可以上传下载文件.可以实现命令行功能,创建和删除文件夹,可以实现的断点续传等功能的FTP服务器.但在 ...
- .NET 同步与异步 之 EventWaitHandle(Event通知) (十三)
本随笔续接:.NET 同步与异步 之 Mutex (十二) 在前一篇我们已经提到过Mutex和本篇的主角们直接或间接继承自 WaitHandle: Mutex类,这个我们在上一篇已经讲过. Event ...
随机推荐
- Android开源日志库Logger的使用
https://blog.csdn.net/Power_Qyh/article/details/78159598?locationNum=2&fps=1 https://github.com/ ...
- Tomcat下指定JDK
- [转] Java基础知识——Java语言基础
http://blog.csdn.net/loneswordman/article/details/9905931 http://blog.csdn.net/wanghuan203/article/d ...
- POJ 1065 Wooden Sticks【贪心】
题意: 有一些木棍,每个有长度和重量,要求把这些木棍排成若干两个属性值均不下降的序列.问至少要分为多少个序列.且要保证排出来的子序列数最少. 思路: ( 9 , 4 ) ,( 2 , 5 ) ,( 1 ...
- ahoi2009维护序列
链接:https://www.luogu.org/problemnew/show/P2023 裸的线段树维护+* 代码: #include <bits/stdc++.h> using na ...
- 解析Linux下\r\n的问题(回车和换行)
http://www.jb51.net/article/37389.htm 深入解析Linux下\r\n的问题 http://www.ruanyifeng.com/blog/2006/04/post_ ...
- 6-2 S树 uva712
这题关键是 反转 查询是固定按照x1x2x3来的 那么先收集前面的顺序 然后在数组里面直接调用即可 比如前面的树是 x3 x1 x2 就把这个当作数组下标 最左边的树是 1<&l ...
- UVA 11426 (欧拉函数&&递推)
题意:给你一个数N,求N以内和N的最大公约数的和 解题思路: 一开始直接想暴力做,4000000的数据量肯定超时.之后学习了一些新的操作. 题目中所要我们求的是N内gcd之和,设s[n]=s[n-1] ...
- 子域名收集之DNS字典爆破工具fierce与dnsdict6的使用
子域名收集之DNS字典爆破工具fierce与dnsdict6的使用 一.fierce 0.介绍 该工具是一个域名扫描综合性工具.它可以快速获取指定域名的DNS服务器,并检查是否存在区域传输(Zone ...
- HTTP协议学习笔记(四)
HTTP协议学习笔记(四) 与 HTTP 协作的 Web 服务器 一台 Web 服务器可搭建多个独立域名的 Web 网站,也可作为通信路径上的中转服务器提升传输效率. 1.用单台虚拟主机实现多个域名 ...