.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

如何使用代碼

備注

盡量通過合理的設計規避離線悲觀鎖,應用場景不會有很多,有使用過的朋友,請留下您寶貴的意見。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM