Fork me on GitHub
防止并发修改 之 离线乐观锁代码示例

防止并发修改 之 离线乐观锁代码示例(菜鸟必读)

背景

小明和小强同时签出了源代码,如果小强先提交,那么提交成功是合理的,接着小明提交了修改,这时源代码服务器就会告诉小明有人在他读取之后做了修改,问他如何处理,源代码服务器会让小明把修改合并后再提交。这就是乐观锁策略,当然源代码服务也可以配置为悲观锁以避免并行修改。

合理的规避并发修改是企业应用中不能回避的问题,但现实场景是,很多团队都回避这个问题。今天我介绍一下如何使用离线乐观锁处理并发修改。

相关文章:再谈在线悲观锁、离线悲观锁、在线乐观锁和离线乐观锁

思路

CAS:Compare And Swap,只有当要修改的值在我读取后没有被修改,才会被交换(修改)。

CAS是多线程领域的术语,比如:无锁的环形队列就是基于这个实现的。因为CAS的思想和乐观锁的思想一致,我就借用一下。

看一下离线乐观锁的应用场景:

上图包含了如下信息:

一、读取线程A1和修改线程A2是不同的线程,因此才叫离线。例如:表单的读取数据和修改。

二:CAS的Compare比较的是版本号,Swap的是整条记录。例如:EntityFramework允许你指定哪些属性是版本属性。

代码示例

设置版本字段

CAS

复制代码
 1         /// <summary>
 2         /// 执行修改。
 3         /// </summary>
 4         public void Handle(TCommand command)
 5         {
 6             var unitOfWork = ServiceLocator.Current.GetInstance<TUnitOfWork>();
 7 
 8             unitOfWork
 9                 .GetRepository<TRepository>()
10                 .MarkAsModified(command.Aggregate);
11 
12             unitOfWork.Commit();
13         }
复制代码

运行效果

注意事项

一、示例中我直接将读取线程读取的数据,离线修改后传递个修改线程了,这样两个线程只有一次读取逻辑,这保证了CAS中Compare的正确性。有些场景你可能需要在修改线程中也进行一次读取,然后将UI层修改的数据合并过来,这种情况就要注意了,必须要手工指定Compare操作使用第一个线程读取的版本号,否则会使用第二次读取的版本号。

二、乐观离线锁只适合重来成本很低的场景,否则用户编辑了两个小时,你告诉他出现并发问题了,他会疯的。这种成本很高的操作适合“离线悲观锁”。

备注

我就是一个行动跟不上思维的人,因为懒惰,我没有全面的在项目中采用乐观锁,下一个项目一定全面实施。

 

posted on 2013-05-29 10:00  HackerVirus  阅读(249)  评论(0编辑  收藏  举报