C#读写锁实例分析
今天小编给大家分享一下C#读写锁实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
ReaderWriterLockSlimReaderWriterLock 类:定义支持单个写线程和多个读线程的锁。
ReaderWriterLockSlim 类:表示用于管理资源访问的锁定状态,可实现多线程读取或进行独占式写入访问。
两者的 API 十分接近,而且 ReaderWriterLockSlim 相对 ReaderWriterLock 来说 更加安全。因此本文主要讲解 ReaderWriterLockSlim 。
两者都是实现多个线程可同时读取、只允许一个线程写入的类。
ReaderWriterLockSlim老规矩,先大概了解一下 ReaderWriterLockSlim 常用的方法。
常用方法ReaderWriterLockSlim 的读、写入锁模板如下:
privatestaticReaderWriterLockSlimtoolLock=newReaderWriterLockSlim();//读privateTRead(){try{toolLock.EnterReadLock();//获取读取锁returnobj;}catch{}finally{toolLock.ExitReadLock();//释放读取锁}returndefault;}//写publicvoidWrite(intkey,intvalue){try{toolLock.EnterUpgradeableReadLock();try{toolLock.EnterWriteLock();/***/}catch{}finally{toolLock.ExitWriteLock();}}catch{}finally{toolLock.ExitUpgradeableReadLock();}}订单系统示例
这里来模拟一个简单粗糙的订单系统。
开始编写代码前,先来了解一些方法的具体使用。
EnterReadLock()
/TryEnterReadLock
和ExitReadLock()
成对出现。
EnterWriteLock()
/TryEnterWriteLock()
和ExitWriteLock()
成对出现。
EnterUpgradeableReadLock()
进入可升级的读模式锁定状态。
EnterReadLock()
使用EnterUpgradeableReadLock()
进入升级状态,在恰当时间点 通过EnterWriteLock()
进入写模式。(也可以倒过来)
定义三个变量:
ReaderWriterLockSlim 多线程读写锁;
MaxId 当前订单 Id 的最大值;
orders 订单表;
privatestaticReaderWriterLockSlimtool=newReaderWriterLockSlim();//读写锁privatestaticintMaxId=1;publicstaticList<DoWorkModel>orders=newList<DoWorkModel>();//订单表
//订单模型publicclassDoWorkModel{publicintId{get;set;}//订单号publicstringUserName{get;set;}//客户名称publicDateTimeDateTime{get;set;}//创建时间}
然后实现查询和创建订单的两个方法。
分页查询订单:
在读取前使用EnterReadLock()
获取锁;
读取完毕后,使用ExitReadLock()
释放锁。
这样能够在多线程环境下保证每次读取都是最新的值。
//分页查询订单privatestaticDoWorkModel[]DoSelect(intpageNo,intpageSize){try{DoWorkModel[]doWorks;tool.EnterReadLock();//获取读取锁doWorks=orders.Skip((pageNo-1)*pageSize).Take(pageSize).ToArray();returndoWorks;}catch{}finally{tool.ExitReadLock();//释放读取锁}returndefault;}
创建订单:
创建订单的信息十分简单,知道用户名和创建时间就行。
订单系统要保证的时每个 Id 都是唯一的(实际情况应该用Guid),这里为了演示读写锁,设置为 数字。
在多线程环境下,我们不使用Interlocked.Increment()
,而是直接使用+= 1
,因为有读写锁的存在,所以操作也是原则性的。
//创建订单privatestaticDoWorkModelDoCreate(stringuserName,DateTimetime){try{tool.EnterUpgradeableReadLock();//升级try{tool.EnterWriteLock();//获取写入锁//写入订单MaxId+=1;//Interlocked.Increment(refMaxId);DoWorkModelmodel=newDoWorkModel{Id=MaxId,UserName=userName,DateTime=time};orders.Add(model);returnmodel;}catch{}finally{tool.ExitWriteLock();//释放写入锁}}catch{}finally{tool.ExitUpgradeableReadLock();//降级}returndefault;}
Main 方法中:
开 5 个线程,不断地读,开 2 个线程不断地创建订单。线程创建订单时是没有设置Thread.Sleep()
的,因此运行速度十分快。
Main 方法里面的代码没有什么意义。
staticvoidMain(string[]args){//5个线程读for(inti=0;i<5;i++){newThread(()=>{while(true){varresult=DoSelect(1,MaxId);if(resultisnull){Console.WriteLine("获取失败");continue;}foreach(variteminresult){Console.Write($"{item.Id}|");}Console.WriteLine("\n");Thread.Sleep(1000);}}).Start();}for(inti=0;i<2;i++){newThread(()=>{while(true){varresult=DoCreate((newRandom().Next(0,100)).ToString(),DateTime.Now);//模拟生成订单if(resultisnull)Console.WriteLine("创建失败");elseConsole.WriteLine("创建成功");}}).Start();}}
在 ASP.NET Core 中,则可以利用读写锁,解决多用户同时发送 HTTP 请求带来的数据库读写问题。
这里就不做示例了。
如果另一个线程发生问题,导致迟迟不能交出写入锁,那么可能会导致其它线程无限等待。
那么可以使用TryEnterWriteLock()
并且设置等待时间,避免阻塞时间过长。
boolisGet=tool.TryEnterWriteLock(500);并发字典写示例
因为理论的东西,笔者这里不会说太多,主要就是先掌握一些 API(方法、属性) 的使用,然后简单写出示例,后面再慢慢深入了解底层原理。
这里来写一个多线程共享使用字典(Dictionary)的使用示例。
增加两个静态变量:
privatestaticReaderWriterLockSlimtoolLock=newReaderWriterLockSlim();privatestaticDictionary<int,int>dict=newDictionary<int,int>();
实现一个写操作:
publicstaticvoidWrite(intkey,intvalue){try{//升级状态toolLock.EnterUpgradeableReadLock();//读,检查是否存在if(dict.ContainsKey(key))return;try{//进入写状态toolLock.EnterWriteLock();dict.Add(key,value);}finally{toolLock.ExitWriteLock();}}finally{toolLock.ExitUpgradeableReadLock();}}
上面没有catch { }
是为了更好观察代码,因为使用了读写锁,理论上不应该出现问题的。
模拟五个线程同时写入字典,由于不是原子操作,所以 sum 的值有些时候会出现重复值。
privatestaticintsum=0;publicstaticvoidAddOne(){for(inti=0;i<100_0000;i++){sum+=1;Write(sum,sum);}}staticvoidMain(string[]args){for(inti=0;i<5;i++)newThread(()=>{AddOne();}).Start();Console.ReadKey();}ReaderWriterLock
大多数情况下都是推荐 ReaderWriterLockSlim 的,而且两者的使用方法十分接近。
例如 AcquireReaderLock 是获取读锁,AcquireWriterLock 获取写锁。使用对应的方法即可替换 ReaderWriterLockSlim 中的示例。
这里就不对 ReaderWriterLock 进行赘述了。
ReaderWriterLock 的常用方法如下:
TimeSpan
超时值将读线程锁升级为写线程锁。以上就是“C#读写锁实例分析”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注亿速云行业资讯频道。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。