本随笔续接:.NET 同步与异步之锁(Lock、Monitor)(七)

由于锁 ( lock 和 Monitor ) 是线程独占式访问的,所以其对性能的影响还是蛮大的,那有没有一种方式可是实现:允许多个线程同时读数据、只允许一个线程写数据呢?答案是肯定的。

读写锁 ReaderWriterLock 、就是 支持单个写线程和多个读线程的锁。自.NET 3.5 开始 ReaderWriterLockSlim、登上舞台,ReaderWriterLockSlim 可以看做是 ReaderWriterLock 的升级版。 由于 ReaderWriterLockSlim  默认不支持递归调用、所以在某种意义上来说更不容易造成死锁。

一、先看一下demo(来源msdn代码示例):

  1. public class SynchronizedCache
  2. {
  3. private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
  4. private Dictionary<int, string> innerCache = new Dictionary<int, string>();
  5.  
  6. public int Count
  7. { get { return innerCache.Count; } }
  8.  
  9. public string Read(int key)
  10. {
  11. cacheLock.EnterReadLock();
  12. try
  13. {
  14. return innerCache[key];
  15. }
  16. finally
  17. {
  18. cacheLock.ExitReadLock();
  19. }
  20. }
  21.  
  22. public void Add(int key, string value)
  23. {
  24. cacheLock.EnterWriteLock();
  25. try
  26. {
  27. innerCache.Add(key, value);
  28. }
  29. finally
  30. {
  31. cacheLock.ExitWriteLock();
  32. }
  33. }
  34.  
  35. public bool AddWithTimeout(int key, string value, int timeout)
  36. {
  37. if (cacheLock.TryEnterWriteLock(timeout))
  38. {
  39. try
  40. {
  41. innerCache.Add(key, value);
  42. }
  43. finally
  44. {
  45. cacheLock.ExitWriteLock();
  46. }
  47. return true;
  48. }
  49. else
  50. {
  51. return false;
  52. }
  53. }
  54.  
  55. public AddOrUpdateStatus AddOrUpdate(int key, string value)
  56. {
  57. cacheLock.EnterUpgradeableReadLock();
  58. try
  59. {
  60. string result = null;
  61. if (innerCache.TryGetValue(key, out result))
  62. {
  63. if (result == value)
  64. {
  65. return AddOrUpdateStatus.Unchanged;
  66. }
  67. else
  68. {
  69. cacheLock.EnterWriteLock();
  70. try
  71. {
  72. innerCache[key] = value;
  73. }
  74. finally
  75. {
  76. cacheLock.ExitWriteLock();
  77. }
  78. return AddOrUpdateStatus.Updated;
  79. }
  80. }
  81. else
  82. {
  83. cacheLock.EnterWriteLock();
  84. try
  85. {
  86. innerCache.Add(key, value);
  87. }
  88. finally
  89. {
  90. cacheLock.ExitWriteLock();
  91. }
  92. return AddOrUpdateStatus.Added;
  93. }
  94. }
  95. finally
  96. {
  97. cacheLock.ExitUpgradeableReadLock();
  98. }
  99. }
  100.  
  101. public void Delete(int key)
  102. {
  103. cacheLock.EnterWriteLock();
  104. try
  105. {
  106. innerCache.Remove(key);
  107. }
  108. finally
  109. {
  110. cacheLock.ExitWriteLock();
  111. }
  112. }
  113.  
  114. public enum AddOrUpdateStatus
  115. {
  116. Added,
  117. Updated,
  118. Unchanged
  119. };
  120.  
  121. ~SynchronizedCache()
  122. {
  123. if (cacheLock != null) cacheLock.Dispose();
  124. }
  125. }
  126.  
  127. private void ReaderWriterLock()
  128. {
  129. var sc = new SynchronizedCache();
  130. var tasks = new List<Task>();
  131. int itemsWritten = ;
  132.  
  133. // Execute a writer.
  134. tasks.Add(Task.Run(() =>
  135. {
  136. String[] vegetables = { "broccoli", "cauliflower",
  137. "carrot", "sorrel", "baby turnip",
  138. "beet", "brussel sprout",
  139. "cabbage", "plantain",
  140. "spinach", "grape leaves",
  141. "lime leaves", "corn",
  142. "radish", "cucumber",
  143. "raddichio", "lima beans" };
  144. for (int ctr = ; ctr <= vegetables.Length; ctr++)
  145. sc.Add(ctr, vegetables[ctr - ]);
  146.  
  147. itemsWritten = vegetables.Length;
  148.  
  149. base.PrintInfo(string.Format("Task {0} wrote {1} items\n", Task.CurrentId, itemsWritten));
  150. }));
  151. // Execute two readers, one to read from first to last and the second from last to first.
  152. for (int ctr = ; ctr <= ; ctr++)
  153. {
  154. bool desc = Convert.ToBoolean(ctr);
  155. tasks.Add(Task.Run(() =>
  156. {
  157. int start, last, step;
  158. int items;
  159. do
  160. {
  161. String output = String.Empty;
  162. items = sc.Count;
  163. if (!desc)
  164. {
  165. start = ;
  166. step = ;
  167. last = items;
  168. }
  169. else
  170. {
  171. start = items;
  172. step = -;
  173. last = ;
  174. }
  175.  
  176. for (int index = start; desc ? index >= last : index <= last; index += step)
  177. output += String.Format("[{0}] ", sc.Read(index));
  178.  
  179. base.PrintInfo(string.Format("Task {0} read {1} items: {2}\n", Task.CurrentId, items, output));
  180.  
  181. } while (items < itemsWritten | itemsWritten == );
  182. }));
  183. }
  184. // Execute a red/update task.
  185. tasks.Add(Task.Run(() =>
  186. {
  187. Thread.Sleep();
  188. for (int ctr = ; ctr <= sc.Count; ctr++)
  189. {
  190. String value = sc.Read(ctr);
  191. if (value == "cucumber")
  192. if (sc.AddOrUpdate(ctr, "green bean") != SynchronizedCache.AddOrUpdateStatus.Unchanged)
  193. base.PrintInfo("Changed 'cucumber' to 'green bean'");
  194. }
  195. }));
  196.  
  197. // Wait for all three tasks to complete.
  198. Task.WaitAll(tasks.ToArray());
  199.  
  200. // Display the final contents of the cache.
  201. base.PrintInfo("");
  202. base.PrintInfo("Values in synchronized cache: ");
  203. for (int ctr = ; ctr <= sc.Count; ctr++)
  204. base.PrintInfo(string.Format(" {0}: {1}", ctr, sc.Read(ctr)));
  205. }

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

  1. .NET 同步与异步之锁(Lock、Monitor)(七)

    本随笔续接:.NET同步与异步之相关背景知识(六) 在上一篇随笔中已经提到.解决竞争条件的典型方式就是加锁 ,那本篇随笔就重点来说一说.NET提供的最常用的锁 lock关键字 和 Monitor. 一 ...

  2. .NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)

    本随笔续接:.NET 同步与异步之锁(ReaderWriterLockSlim)(八) 之前的随笔已经说过.加锁虽然能很好的解决竞争条件,但也带来了负面影响:性能方面的负面影响.那有没有更好的解决方案 ...

  3. CIL锁,GIL与线程池的区别,进程池和线程池,同步与异步

    一.GIL锁 什么是GIL? 全局解释器锁,是加在解释器上的互斥锁 GC是python自带的内存管理机制,GC的工作原理:python中的内存管理使用的是应用计数,每个数会被加上一个整型的计数器,表示 ...

  4. [多线程]线程基础(对象锁、class锁、同步、异步)

    synchronized.volatile.ReentrantLock.concurrent 线程安全:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法) ...

  5. .NET同步与异步之相关背景知识(六)

    在之前的五篇随笔中,已经介绍了.NET 类库中实现并行的常见方式及其基本用法,当然.这些基本用法远远不能覆盖所有,也只能作为一个引子出现在这里.以下是前五篇随笔的目录: .NET 同步与异步之封装成T ...

  6. input屏蔽历史记录 ;function($,undefined) 前面的分号是什么用处 JSON 和 JSONP 两兄弟 document.body.scrollTop与document.documentElement.scrollTop兼容 URL中的# 网站性能优化 前端必知的ajax 简单理解同步与异步 那些年,我们被耍过的bug——has

    input屏蔽历史记录   设置input的扩展属性autocomplete 为off即可 ;function($,undefined) 前面的分号是什么用处   ;(function($){$.ex ...

  7. 半同步半异步模式的实现 - MSMQ实现

    半同步半异步模式的实现 - MSMQ实现 所谓半同步半异步是指,在某个方法调用中,有些代码行是同步执行方式,有些代码行是异步执行方式,下面我们来举个例子,还是以经典的PlaceOrder来说,哈哈. ...

  8. python并发编程(并发与并行,同步和异步,阻塞与非阻塞)

    最近在学python的网络编程,学了socket通信,并利用socket实现了一个具有用户验证功能,可以上传下载文件.可以实现命令行功能,创建和删除文件夹,可以实现的断点续传等功能的FTP服务器.但在 ...

  9. .NET 同步与异步 之 EventWaitHandle(Event通知) (十三)

    本随笔续接:.NET 同步与异步 之 Mutex (十二) 在前一篇我们已经提到过Mutex和本篇的主角们直接或间接继承自 WaitHandle: Mutex类,这个我们在上一篇已经讲过. Event ...

随机推荐

  1. Android开源日志库Logger的使用

    https://blog.csdn.net/Power_Qyh/article/details/78159598?locationNum=2&fps=1 https://github.com/ ...

  2. Tomcat下指定JDK

  3. [转] Java基础知识——Java语言基础

    http://blog.csdn.net/loneswordman/article/details/9905931 http://blog.csdn.net/wanghuan203/article/d ...

  4. POJ 1065 Wooden Sticks【贪心】

    题意: 有一些木棍,每个有长度和重量,要求把这些木棍排成若干两个属性值均不下降的序列.问至少要分为多少个序列.且要保证排出来的子序列数最少. 思路: ( 9 , 4 ) ,( 2 , 5 ) ,( 1 ...

  5. ahoi2009维护序列

    链接:https://www.luogu.org/problemnew/show/P2023 裸的线段树维护+* 代码: #include <bits/stdc++.h> using na ...

  6. 解析Linux下\r\n的问题(回车和换行)

    http://www.jb51.net/article/37389.htm 深入解析Linux下\r\n的问题 http://www.ruanyifeng.com/blog/2006/04/post_ ...

  7. 6-2 S树 uva712

    这题关键是  反转    查询是固定按照x1x2x3来的   那么先收集前面的顺序  然后在数组里面直接调用即可 比如前面的树是 x3 x1 x2  就把这个当作数组下标 最左边的树是 1<&l ...

  8. UVA 11426 (欧拉函数&&递推)

    题意:给你一个数N,求N以内和N的最大公约数的和 解题思路: 一开始直接想暴力做,4000000的数据量肯定超时.之后学习了一些新的操作. 题目中所要我们求的是N内gcd之和,设s[n]=s[n-1] ...

  9. 子域名收集之DNS字典爆破工具fierce与dnsdict6的使用

    子域名收集之DNS字典爆破工具fierce与dnsdict6的使用 一.fierce 0.介绍 该工具是一个域名扫描综合性工具.它可以快速获取指定域名的DNS服务器,并检查是否存在区域传输(Zone ...

  10. HTTP协议学习笔记(四)

    HTTP协议学习笔记(四) 与 HTTP 协作的 Web 服务器 一台 Web 服务器可搭建多个独立域名的 Web 网站,也可作为通信路径上的中转服务器提升传输效率. 1.用单台虚拟主机实现多个域名 ...