.NET:离线悲观锁 之 过期策略支持
2013-06-05 08:11
232 查看
背景
之前写了一篇文章防止并发修改 之 离线悲观锁代码示例(离线悲观锁),这篇文章回避了一个问题,就是如何处理用户直接关闭浏览器后导致的锁占用问题。本文就介绍一个思路。思路
思路1
这是之前已经提供过的思路,只是没有贴出来,就是:当会话结束的时候清除所有用户持有的锁,这会导致个别锁在会话期间被长时间占用(可能超过几个小时)。思路2
引入一个后台线程,每隔指定的分钟就清理一下被长时间占用的锁,如:清理那些占用超过10分钟的锁,这回导致一定的线程成本,因为这个线程需要频繁的运行。思路3
引入过期策略,是否被锁完全取决于两个条件:是否拥有锁以及是否过期,这个思路下过期的锁会成为一种垃圾,如何清理这种垃圾又是一个问题,我们可以每6个小时清理一次或引入环形字典。基于过期策略的实现
类图
代码
基于内存的离线悲观锁管理器using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Happy.DesignByContract; using Happy.Application.PessimisticLock.Internal; namespace Happy.Application.PessimisticLock { /// <summary> /// 基于内存的离线悲观锁管理器。 /// </summary> public sealed class MemoryLockManager : ILockManager { private static readonly Dictionary<string, LockItem> _items = new Dictionary<string, LockItem>(); /// <inheritdoc /> public bool AcquireLock(string entity, string key, string owner, IExpirationPolicy expirationPolicy) { entity.MustNotNullAndNotWhiteSpace("entity"); key.MustNotNullAndNotWhiteSpace("key"); owner.MustNotNullAndNotWhiteSpace("owner"); expirationPolicy.MustNotNull("expirationPolicy"); var item = LockItem.Crete(entity, key, owner, expirationPolicy); lock (_items) { if (!IsLocked(item.Identifier)) { LockIt(item); return true; } return IsLockedBy(item.Identifier, item.Owner); } } /// <inheritdoc /> public void ReleaseLock(string entity, string key, string owner) { entity.MustNotNullAndNotWhiteSpace("entity"); key.MustNotNullAndNotWhiteSpace("key"); owner.MustNotNullAndNotWhiteSpace("owner"); var identifier = LockItem.CreateIdentifier(entity, key); lock (_items) { if (!IsLockedBy(identifier, owner)) { throw new InvalidOperationException(string.Format(Messages.Error_CanNotReleaseLock, owner)); } ReleaseLock(identifier); } } /// <inheritdoc /> public void ReleaseLocks(string owner) { lock (_items) { foreach (var keypair in _items) { if (keypair.Value.Owner == owner) { ReleaseLock(keypair.Value.Identifier); } } } } /// <inheritdoc /> public void ReleaseExpiredLocks() { lock (_items) { foreach (var keypair in _items) { if (keypair.Value.ExpirationPolicy.IsExpired()) { ReleaseLock(keypair.Value.Identifier); } } } } private static bool IsLocked(string identifier) { return _items.ContainsKey(identifier) && !_items[identifier].ExpirationPolicy.IsExpired(); } private static bool IsLockedBy(string identifier, string owner) { if (!IsLocked(identifier)) { return false; } return _items[identifier].Owner == owner; } private static void LockIt(LockItem item) { _items[item.Identifier] = item; } private static void ReleaseLock(string identifier) { _items.Remove(identifier); } } }
基于时间的过期策略
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Happy.Application.PessimisticLock { /// <summary> /// 基于时间的过期策略。 /// </summary> [Serializable] public class DateTimeExpirationPolicy : IExpirationPolicy { private readonly DateTime _start = DateTime.Now; private readonly TimeSpan _expiration; /// <summary> /// 构造方法。 /// </summary> /// <param name="expiration">过期时间间隔</param> public DateTimeExpirationPolicy(TimeSpan expiration) { _expiration = expiration; } /// <summary> /// 构造方法。 /// </summary> /// <param name="minute">过期的分钟</param> public DateTimeExpirationPolicy(uint? minute) { _expiration = TimeSpan.FromMinutes((double)minute); } /// <summary> /// 是否过期。 /// </summary> public bool IsExpired() { return (DateTime.Now - _start) > _expiration; } } }
每隔6小时进行一次垃圾清理
var lockManager = BootstrapService.Current.Container.GetInstance<ILockManager>(); var timer = new Timer(state => { lockManager.ReleaseExpiredLocks(); }, null, 1000 * 60 * 6, 1000 * 60 * 6);
备注
早上来的路上想到一个思路可以避免6小时清理一下垃圾,就是使用环形字典,找个时间我试试。注意:6小时清理一次垃圾,并不代表6小时才过期的。
相关文章推荐
- 离线悲观锁 之 过期策略支持
- .NET:防止并发修改 之 离线悲观锁代码示例(离线悲观锁)
- .NET:再谈在线悲观锁、离线悲观锁、在线乐观锁和离线乐观锁。
- .NET:防止并发修改 之 离线悲观锁代码示例(离线悲观锁)
- .NET:在线悲观锁、在线乐观锁、离线悲观锁、离线乐观锁代码示例
- .NET:在线悲观锁、在线乐观锁、离线悲观锁、离线乐观锁代码示例
- 加密解密概述及.NET中对加密解密的支持(二)
- 基于.NET 2.0的GIS开源项目SharpMap分析手记(七):GDAL C#接口库的编译与SharpMap(Change Set 21021)对影像的支持研究
- GC悲观策略之Serial GC篇
- MySQLDriverCS 不支持问题解决 Uint 4.0 .net 2.0
- .NET策略(Strategy)模式
- Linux 下博客离线发布客户端 Blogilo,支持CSDN
- 悲观的并发策略——Synchronized互斥锁
- 离线安装.net 3.5
- WCF Data Service 的.NET Client 的不支持原生类型服务操作的解决方法
- WCF Data Service 的.NET Client 的不支持原生类型服务操作的解决方法
- WCF Data Service 的.NET Client 的不支持原生类型服务操作的解决方法
- WCF Data Service 的.NET Client 的不支持原生类型服务操作的解决方法
- 关于p3p 简洁策略,以及浏览器的支持情况.
- 艾伟_转载:VS 2010 和 .NET 4.0 系列之《多显示器支持》篇