[.net 多线程 ]ReaderWriterLock
ReaderWriterLock 用于同步对资源的访问。在任一特定时刻,它允许多个线程同时进行读访问,或者允许单个线程进行写访问。在资源不经常发生更改的情况下,ReaderWriterLock 所提供的吞吐量比简单的一次只允许一个线程的锁(如 Monitor)更高。
在多数访问为读访问,而写访问频率较低、持续时间也比较短的情况下,ReaderWriterLock 的性能最好。多个读线程与单个写线程交替进行操作,所以读线程和写线程都不会长时间阻止。
注意
长时间持有读线程锁或写线程锁会使其他线程发生饥饿 (starve)。为了得到最好的性能,需要考虑重新构造应用程序以将写访问的持续时间减少到最小。
一个线程可以持有读线程锁或写线程锁,但是不能同时持有两者。若要获取写线程锁,请使用 UpgradeToWriterLock 和 DowngradeFromWriterLock,而不要通过释放读线程锁的方式获取。
递归锁请求会增加锁上的锁计数。
读线程和写线程将分别排入各自的队列。当线程释放写线程锁时,此刻读线程队列中的所有等待线程都将被授予读线程锁;当已释放所有读线程锁时,写线程队列中处于等待状态的下一个线程(如果存在)将被授予写线程锁,依此类推。换句话说,ReaderWriterLock 在一组读线程和一个写线程之间交替进行操作。
当写线程队列中有一个线程在等待活动读线程锁被释放时,请求新的读线程锁的线程会排入读线程队列。即使它们能和现有的阅读器锁持有者共享并发访问,也不会给它们的请求授予权限;这有助于防止编写器被阅读器无限期阻止。
大多数在 ReaderWriterLock 上获取锁的方法都采用超时值。使用超时可以避免应用程序中出现死锁。例如,某个线程可能获取了一个资源上的写线程锁,然后请求第二个资源上的读线程锁;同时,另一个线程获取了第二个资源上的写线程锁,并请求第一个资源上的读线程锁。如果不使用超时,这两个线程将出现死锁。
如果超时间隔过期并且没有授予锁请求,则此方法通过引发 ApplicationException 将控制返回给调用线程。线程可以捕捉此异常并确定下一步要进行的操作。
static void Main(string[] args)
{
Dictionary<int, string> dic = new Dictionary<int, string>();
ReaderWriterLockSlim lockSlim = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
Task.Factory.StartNew(() =>
{
int key = ;
while (key <= )
{
Thread.Sleep();
string info = $"写线程:{DateTime.Now.ToString("hhMMss-fff")}";
if (lockSlim.TryEnterWriteLock())
{
try
{
dic.Add(key++, info);
Console.WriteLine(info);
}
catch (Exception ex)
{
;
}
finally
{
Console.WriteLine($"释放 {info}");
lockSlim.ExitWriteLock();
}
}
}
});
Task.Factory.StartNew(() =>
{
while (true)
{
Thread.Sleep();
int key = ;
if (lockSlim.TryEnterReadLock())
{
try
{
while (key < dic.Count)
{
Console.WriteLine($"读线程1:{dic[key++]}");
}
}
catch (Exception ex)
{
;
}
finally
{
lockSlim.ExitReadLock();
}
}
}
});
Task.Factory.StartNew(() =>
{
while (true)
{
Thread.Sleep();
int key = ;
if (lockSlim.TryEnterReadLock())
{
try
{
while (key < dic.Count)
{
Console.WriteLine($"读线程2:{dic[key++]}");
}
}
catch (Exception ex)
{
;
}
finally
{
lockSlim.ExitReadLock();
}
}
}
});
Task.Factory.StartNew(() =>
{
while (true)
{
Thread.Sleep();
int key = ;
if (lockSlim.TryEnterReadLock())
{
try
{
while (key < dic.Count)
{
Console.WriteLine($"读线程3:{dic[key++]}");
}
}
catch (Exception ex)
{
;
}
finally
{
lockSlim.ExitReadLock();
}
}
}
});
Task.Factory.StartNew(() =>
{
while (true)
{
Thread.Sleep();
int key = ;
if (lockSlim.TryEnterReadLock())
{
try
{
while (key < dic.Count)
{
Console.WriteLine($"读线程4:{dic[key++]}");
}
}
catch (Exception ex)
{
;
}
finally
{
lockSlim.ExitReadLock();
}
}
}
});
Console.ReadKey();
}
.NET 同步与异步之锁(ReaderWriterLockSlim)(八)
c#线程同步系列(二) c#中ReaderWriterLock的使用
读写锁ReaderWriterLockSlim
[.net 多线程 ]ReaderWriterLock的更多相关文章
- C#多线程---ReaderWriterLock实现线程同步
一.简介 当我们需要对一个共享资源多次读取的时候,用前面Monitor的同步锁就没有必要了.因为同步锁每次只允许一个线程访问共享资源,其他线程都会阻塞. 此时,通过ReaderWriterLock类可 ...
- 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock
[源码下载] 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLoc ...
- C#多线程:使用ReaderWriterLock类实现多用户读/单用户写同步
摘要:C#提供了System.Threading.ReaderWriterLock类以适应多用户读/单用户写的场景.该类可实现以下功能:如果资源未被写操作锁定,那么任何线程都可对该资源进行读操作锁定, ...
- 多线程中的锁系统(二)-volatile、Interlocked、ReaderWriterLockSlim
上章主要讲排他锁的直接使用方式.但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了,本篇主要介绍下升级锁和原子操作. 阅读目录 volatile Interlocked ReaderWriterLo ...
- ReaderWriterLock的UpgradeToWriterLock方法的一种使用场景
ReaderWriterLock对比互斥锁(lock)的优势是,读锁和写锁的分离,读锁之间互不排斥. 当然,本文重点不是讲ReaderWriterLock本身,而是讲它的UpgradeToWriter ...
- C#多线程技术总结(同步)
二.串行(同步): 1.lock.Monitor--注意锁定的对象必需是引用类型(string类型除外) 示例: private static object syncObject = new obje ...
- C#多线程总结
线程的创建 Thread var thread = new Thread(() => { Console.WriteLine("thread start:" + Thread ...
- C#多线程操作界面控件的解决方案(转)
C#中利用委托实现多线程跨线程操作 - 张小鱼 2010-10-22 08:38 在使用VS2005的时候,如果你从非创建这个控件的线程中访问这个控件或者操作这个控件的话就会抛出这个异常.这是微软为了 ...
- 【五子棋AI循序渐进】——多线程搜索
关于多线程搜索,有很多方法来实现,很多文章推荐基于MTD(F)的方式.好处不言而喻,不过我的程序中采用的是基于PVS的多线程搜索.实现起来主要是这几个方面问题需要解决: 1.置换表的互斥访问. 2.局 ...
随机推荐
- 88EE1111 PHY芯片设置
本次调试88EE1111 PHY芯片之主要目的主要对应为了将其默认的GMII接口通过配置成RGMII接口.因此,可能本文档涉及到的内容并没有涉及到PHY芯片的88EE1111所有内容. PHY芯片管理 ...
- java - 判断 字符串是否为空
java - 判断 字符串是否为空 方法一: 最多人使用的一个方法, 直观, 方便, 但效率很低.1:if(s == null || s.equals(""));方法二: 比较字符 ...
- infos
C++文件流 iostream 提供了cin cout 分别用于从标准输入读取流和向标准输出写入流cout 标准输出 屏幕 输出写入fstream文件流 cin 从标准输入读取cout 向标准输入写入 ...
- 前端自动化之gulp
前端自动化之gulp 前题:1.安装好nodejs环境,或者nvm 2.安装npm包管理工具 简介: 可以自动的将开发阶段的代码进行压缩.合并.编译.加密等处理,生成项目提交的代码. 使用: gulp ...
- scala 在vim中的语法高亮
https://github.com/derekwyatt/vim-scala 提供了一个选择 看install手册,发现两个命令都是必须的. mkdir -p ~/.vim/{ftdetect,in ...
- LoadRunner简明教程
LoadRunner是什么 LoadRunner是一个性能测试工具,它最初是Mercury公司的产品,后背HP收购. LoadRunner常用来做什么 l 验证某系统在某环境下是否满足性能需求. l ...
- SpringBoot RestController 同时支持返回xml和json格式数据
@RestController 默认支持返回json格式数据,即使不做任何配置也能返回json数据 当接口需要支持xml或json两种格式数据时应该怎么做呢? 只要引入 Jackson xml的 ma ...
- c语言实践:RS信号报告
题目: 无线电台的RS制信号报告是由三两个部分组成的: R(Readability) 信号可辨度即清晰度. S(Strength) 信号强度即大小. 其中R位于报告第一位,共分5级,用1—5数字 ...
- c语言学习笔记 break语句
比如 for() { for() { break; } } 那个break语句只是跳出它所在的那个for循环,不会把最外面的for循环都跳出去.
- GPS通讯协议协议(NMEA0183)
一.简介 GPS(全球定位系统)接收机与手持机之间的数据交换格式一般都由生产厂商缺省定制,其定义内容普通用户很难知晓,且不同品牌.不同型号的GPS接收机所配置的控制应用程序也因生产厂家的不同而不同.所 ...