离线悲观锁 之 过期策略支持
离线悲观锁 之 过期策略支持
背景
之前写了一篇文章 防止并发修改 之 离线悲观锁代码示例(离线悲观锁) ,这篇文章回避了一个问题,就是如何处理用户直接关闭浏览器后导致的锁占用问题。本文就介绍一个思路。
思路
思路1
这是之前已经提供过的思路,只是没有贴出来,就是:当会话结束的时候清除所有用户持有的锁,这会导致个别锁在会话期间被长时间占用(可能超过几个小时)。
思路2
引入一个后台线程,每隔指定的分钟就清理一下被长时间占用的锁,如:清理那些占用超过10分钟的锁,这回导致一定的线程成本,因为这个线程需要频繁的运行。
思路3
引入过期策略,是否被锁完全取决于两个条件:是否拥有锁以及是否过期,这个思路下过期的锁会成为一种垃圾,如何清理这种垃圾又是一个问题,我们可以每6个小时清理一次或引入环形字典。
基于过期策略的实现
类图
代码
基于内存的离线悲观锁管理器
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 , LockItem> _items = new Dictionary< string , LockItem> ();
18
19 /// <inheritdoc />
20 public bool AcquireLock( string entity, string key, string owner, IExpirationPolicy expirationPolicy)
21 {
22 entity.MustNotNullAndNotWhiteSpace( " entity " );
23 key.MustNotNullAndNotWhiteSpace( " key " );
24 owner.MustNotNullAndNotWhiteSpace( " owner " );
25 expirationPolicy.MustNotNull( " expirationPolicy " );
26
27 var item = LockItem.Crete(entity, key, owner, expirationPolicy);
28
29 lock (_items)
30 {
31 if (! IsLocked(item.Identifier))
32 {
33 LockIt(item);
34
35 return true ;
36 }
37
38 return IsLockedBy(item.Identifier, item.Owner);
39 }
40 }
41
42 /// <inheritdoc />
43 public void ReleaseLock( string entity, string key, string owner)
44 {
45 entity.MustNotNullAndNotWhiteSpace( " entity " );
46 key.MustNotNullAndNotWhiteSpace( " key " );
47 owner.MustNotNullAndNotWhiteSpace( " owner " );
48
49 var identifier = LockItem.CreateIdentifier(entity, key);
50 lock (_items)
51 {
52 if (! IsLockedBy(identifier, owner))
53 {
54 throw new InvalidOperationException( string .Format(Messages.Error_CanNotReleaseLock, owner));
55 }
56
57 ReleaseLock(identifier);
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 ReleaseLock(keypair.Value.Identifier);
71 }
72 }
73 }
74 }
75
76 /// <inheritdoc />
77 public void ReleaseExpiredLocks()
78 {
79 lock (_items)
80 {
81 foreach ( var keypair in _items)
82 {
83 if (keypair.Value.ExpirationPolicy.IsExpired())
84 {
85 ReleaseLock(keypair.Value.Identifier);
86 }
87 }
88 }
89 }
90
91 private static bool IsLocked( string identifier)
92 {
93 return
94 _items.ContainsKey(identifier)
95 &&
96 ! _items[identifier].ExpirationPolicy.IsExpired();
97 }
98
99 private static bool IsLockedBy( string identifier, string owner)
100 {
101 if (! IsLocked(identifier))
102 {
103 return false ;
104 }
105
106 return _items[identifier].Owner == owner;
107 }
108
109 private static void LockIt(LockItem item)
110 {
111 _items[item.Identifier] = item;
112 }
113
114 private static void ReleaseLock( string identifier)
115 {
116 _items.Remove(identifier);
117 }
118 }
119 }
基于时间的过期策略
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 [Serializable]
13 public class DateTimeExpirationPolicy : IExpirationPolicy
14 {
15 private readonly DateTime _start = DateTime.Now;
16 private readonly TimeSpan _expiration;
17
18 /// <summary>
19 /// 构造方法。
20 /// </summary>
21 /// <param name="expiration"> 过期时间间隔 </param>
22 public DateTimeExpirationPolicy(TimeSpan expiration)
23 {
24 _expiration = expiration;
25 }
26
27 /// <summary>
28 /// 构造方法。
29 /// </summary>
30 /// <param name="minute"> 过期的分钟 </param>
31 public DateTimeExpirationPolicy( uint ? minute)
32 {
33 _expiration = TimeSpan.FromMinutes(( double )minute);
34 }
35
36 /// <summary>
37 /// 是否过期。
38 /// </summary>
39 public bool IsExpired()
40 {
41 return (DateTime.Now - _start) > _expiration;
42 }
43 }
44 }
每隔6小时进行一次垃圾清理
1 var lockManager = BootstrapService.Current.Container.GetInstance<ILockManager> ();
2 var timer = new Timer(state =>
3 {
4 lockManager.ReleaseExpiredLocks();
5 }, null , 1000 * 60 * 6 , 1000 * 60 * 6 );
备注
早上来的路上想到一个思路可以避免6小时清理一下垃圾,就是使用环形字典,找个时间我试试。
注意:6小时清理一次垃圾,并不代表6小时才过期的。
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did45452