好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

离线悲观锁 之 过期策略支持

离线悲观锁 之 过期策略支持

离线悲观锁 之 过期策略支持

背景

 

之前写了一篇文章 防止并发修改 之 离线悲观锁代码示例(离线悲观锁) ,这篇文章回避了一个问题,就是如何处理用户直接关闭浏览器后导致的锁占用问题。本文就介绍一个思路。

 

思路

 

思路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://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于离线悲观锁 之 过期策略支持的详细内容...

  阅读:41次