.NET:防止并发修改 之 离线悲观锁代码示例(离线悲观锁)

背景

系统会出现并发,上篇文章我介绍了如何使用“离线乐观锁”保证并发,离线乐观锁适合处理那些重新编辑成本不大的单据,如果某个单据用户花了10分钟进行编辑,提交时你告诉他出现并发了,他心里肯定会骂娘的,今天介绍的“离线悲观锁”就可以避免这种情况。

思路

小明签出了源代码,小强就不能签出了,我们目前的源代码系统就是用的这种悲观策略。

实现

核心代码

离线悲观锁管理器接口

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Happy.Application.PessimisticLock
 8 {
 9     /// <summary>
10     /// 离线悲观锁管理器接口。
11     /// </summary>
12     public interface ILockManager
13     {
14         /// <summary>
15         /// 获取锁。
16         /// </summary>
17         /// <param name="entity">锁的主体,如:表名或聚合名。</param>
18         /// <param name="key">锁的主体的唯一标识,如:主键或唯一索引。</param>
19         /// <param name="owner">锁的拥有者,如:UserId或SessionId。</param>
20         /// <returns>获取锁成功就返回true,否则返回false。</returns>
21         bool AcquireLock(string entity, string key, string owner);
22 
23         /// <summary>
24         /// 释放锁。
25         /// </summary>
26         /// <param name="entity">锁的主体,如:表名或聚合名。</param>
27         /// <param name="key">锁的主体的唯一标识,如:主键或唯一索引。</param>
28         /// <param name="owner">锁的拥有者,如:UserId或SessionId。</param>
29         void ReleaseLock(string entity, string key, string owner);
30 
31 
32         /// <summary>
33         /// 释拥有者的所有锁。
34         /// </summary>
35         /// <param name="owner">锁的拥有者,如:UserId或SessionId。</param>
36         void ReleaseLocks(string owner);
37     }
38 }
复制代码

基于内存的离线悲观锁管理器

复制代码
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 
  7 using Happy.DesignByContract;
  8 using Happy.Application.PessimisticLock.Internal;
  9 
 10 namespace Happy.Application.PessimisticLock
 11 {
 12     /// <summary>
 13     /// 基于内存的离线悲观锁管理器。
 14     /// </summary>
 15     public sealed class MemoryLockManager : ILockManager
 16     {
 17         private static readonly Dictionary<string, MemoryLockItem> _items = new Dictionary<string, MemoryLockItem>();
 18 
 19         /// <inheritdoc />
 20         public bool AcquireLock(string entity, string key, string owner)
 21         {
 22             entity.MustNotNullAndNotWhiteSpace("entity");
 23             key.MustNotNullAndNotWhiteSpace("key");
 24             owner.MustNotNullAndNotWhiteSpace("owner");
 25 
 26             var item = MemoryLockItem.Crete(entity, key, owner);
 27 
 28             lock (_items)
 29             {
 30                 if (!IsLocked(item.Identifier))
 31                 {
 32                     SetLockItem(item);
 33 
 34                     return true;
 35                 }
 36 
 37                 return IsLockedBy(item);
 38             }
 39         }
 40 
 41         /// <inheritdoc />
 42         public void ReleaseLock(string entity, string key, string owner)
 43         {
 44             entity.MustNotNullAndNotWhiteSpace("entity");
 45             key.MustNotNullAndNotWhiteSpace("key");
 46             owner.MustNotNullAndNotWhiteSpace("owner");
 47 
 48             var item = MemoryLockItem.Crete(entity, key, owner);
 49 
 50             lock (_items)
 51             {
 52                 if (!IsLockedBy(item))
 53                 {
 54                     throw new InvalidOperationException(string.Format(Messages.Error_CanNotReleaseLock, owner));
 55                 }
 56 
 57                 RemoveLockItem(item);
 58             }
 59         }
 60 
 61         /// <inheritdoc />
 62         public void ReleaseLocks(string owner)
 63         {
 64             lock (_items)
 65             {
 66                 foreach (var keypair in _items)
 67                 {
 68                     if (keypair.Value.Owner == owner)
 69                     {
 70                         RemoveLockItem(keypair.Value);
 71                     }
 72                 }
 73             }
 74         }
 75 
 76         private static bool IsLocked(string identifier)
 77         {
 78             return _items.ContainsKey(identifier);
 79         }
 80 
 81         private static void SetLockItem(MemoryLockItem item)
 82         {
 83             _items[item.Identifier] = item;
 84         }
 85 
 86         private static bool IsLockedBy(MemoryLockItem item)
 87         {
 88             if (!IsLocked(item.Identifier))
 89             {
 90                 return false;
 91             }
 92 
 93             return _items[item.Identifier].Owner == item.Owner;
 94         }
 95 
 96         private static void RemoveLockItem(MemoryLockItem item)
 97         {
 98             _items.Remove(item.Identifier);
 99         }
100     }
101 }
复制代码

离线悲观锁代理

复制代码
 1 /**
 2  * 离线悲观锁代理。
 3  * 
 4  * @static
 5  * @class PessimisticLockProxy
 6  * @namespace Happy.server
 7  */
 8 Ext.define('Happy.server.PessimisticLockProxy', {
 9     alternateClassName: ['PessimisticLockProxy'],
10     singleton: true,
11     requires: ['Happy.Ajax'],
12 
13     acquireLock: function (entity, key, success, failure) {
14         var me = this;
15 
16         Happy.Ajax.callAction({
17             url: '/LockManager/AcquireLock',
18             params: { entity: entity, key: key },
19             success: success,
20             failure: failure
21         });
22     },
23 
24     releaseLock: function (entity, key, success, failure) {
25         var me = this;
26 
27         Happy.Ajax.callAction({
28             url: '/LockManager/ReleaseLock',
29             params: { entity: entity, key: key },
30             success: success,
31             failure: failure
32         });
33     }
34 });
复制代码

运行效果

 

代码下载

地址:http://happy.codeplex.com/SourceControl/latest。因为项目正在重构中,请下载最新源代码,不要下载Happy-1.0.0.3

如何使用代码

备注

尽量通过合理的设计规避离线悲观锁,应用场景不会有很多,有使用过的朋友,请留下您宝贵的意见。

转载自http://www.cnblogs.com/happyframework/archive/2013/05/30/3107389.html

posted @ 2013-05-30 11:14  沙耶  阅读(397)  评论(0编辑  收藏  举报