本随笔续接:.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)(八)的更多相关文章

  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. 《Kafka技术内幕》学习笔记

    第一章 Kafka入门 1.1 Kafka流式数据平台 Kafka作为流式数据平台的特点: 消息系统:两种消息模型:队列和发布订阅. 队列模型:将处理工作平均分给消费组中的消费者成员. 发布订阅模型: ...

  2. T9 HDU1298

    就是字典树加dfs 把所有操作封在结构体里面 #include <cstdio> #include <cstring> #include <algorithm> # ...

  3. macos 下安装virtualenv,virtualenvwrapper,然后在pycharm中正常配置方法日志

    1.安装virtualenv或virtualenvwrapper pip install virtualenv pip install virtualenvwraper 注意pip的版本号(查看 pi ...

  4. Spring根据包名获取包路径下的所有类

    参考mybatis MapperScannerConfigurer.java 最终找到 Spring的一个类  ClassPathBeanDefinitionScanner.java 参考ClassP ...

  5. 在Spring中配置SQL server 2000

    前言 Lz主要目的是在Spring中配置SQL server 2000数据库,但实现目的的过程中参差着许多SQL server 2000的知识,也包罗在本文记载下来!(Lz为什么要去搞sql serv ...

  6. 小程序使用 rpx 单位 转 px的方法(用于动画、canvas画图)

    1.需要借助的API:wx.getSystemInfoSync(); 通过API可获取的值: // 在 iPhone6 下运行: var systemInfo = wx.getSystemInfoSy ...

  7. C++泛型线性查找算法——find

    C++泛型线性查找算法--find <泛型编程和STL>笔记及思考. 线性查找可能是最为简单的一类查找算法了.他所作用的数据结构为一维线性的空间.这篇文章主要介绍使用 C++ 实现泛型算法 ...

  8. Window环境下,PHP调用Python脚本

    参考 php调用python脚本*** php 调用 python脚本的方法 解决办法:php提供了许多调用其他脚本或程序的方法,比如exec/system/popen/proc_open/passt ...

  9. Web大前端面试题-Day12

    1.前端需要注意哪些SEO? 合理的title.description.keywords: 搜索对着三项的权重逐个减小, title值强调重点即可, 重要关键词出现不要超过2次, 而且要靠前, 不同页 ...

  10. flask第十篇——url_for【3】

      继续url_for的知识点 # coding: utf-8 from flask import Flask, url_forimport flask app = Flask(__name__)   ...