线程安全的XpoSequencer

其实应该算是补遗了,之前的一篇随笔曾经介绍了 如何在XPO中为非主键字段获取Int型自增量 ,但今次在实际应用中发现该段代码是有问题的。

 

最大的问题,它并不线程安全的,这一点在今天的一个Parallel.For测试中暴露无遗。

 

原代码使用一个MaxIdGenerationAttempts = 7的常量来控制重试次数,如果发现当前保存操作引发了LockingException则歇一段时间重试。这样有2个问题,一来它并没有根本上解决线程安全问题,在并发量稍高一点的情况下,某条不走运的线程重试了7次依然无法成功完成操作后还是会引发异常;二来多次的读写尝试耗费了大量的时间。加大这个常量可以延后引发异常的时间点(或者说降低一点概率),但明显依旧无法根本上解决问题,并且失败但仍旧占用了资源的读写操作也更多。

 

改动其实也简单,加一个线程锁就行。顺带我把model独立出来了,个人比较喜欢这样,看起来清爽。另外也改成了泛型的方法,用起来稍方便点。 

 

 

     public   sealed   class  Sequencer
    {
        
private   readonly   static   object  locker  =   new   object ();

        
public   static   uint  GetNextValue < T > (IDataLayer dataLayer) 
        {
            
if (dataLayer  ==   null )
                
throw   new  ArgumentNullException( " dataLayer " );

            
lock  (locker)
            {
                
using  (Session session  =   new  Session(dataLayer))
                {
                    
string  typename  =  session.GetClassInfo < T > ().FullName;

                    XpoSequence seq 
=  session.FindObject < XpoSequence > (XpoSequence.Fields.TypeName  ==  typename);
                    
if  (seq  ==   null )
                    {
                        seq 
=   new  XpoSequence(session);
                        seq.TypeName 
=  typename;
                    }
                    seq.CurrentID
++ ;
                    seq.Save();
                    
return  seq.CurrentID;
                }
            }
        }
        
public   static   uint  GetNextValue < T > () 
        {
            
return  GetNextValue < T > (XpoDefault.DataLayer);
        }
    }

 

 

    [Persistent( " Sequence " )]
    
public   class  XpoSequence : XPBaseObject
    {
        
public  XpoSequence()
            : 
base ()
        {
            
//  This constructor is used when an object is loaded from a persistent storage.
            
//  Do not place any code here.
        }

        
public  XpoSequence(Session session)
            : 
base (session)
        {
            
//  This constructor is used when an object is loaded from a persistent storage.
            
//  Do not place any code here.
        }

        
public   override   void  AfterConstruction()
        {
            
base .AfterConstruction();
            
//  Place here your initialization code.
        }

        
//  Fields...
         private   uint  _CurrentID;
        
private   string  _TypeName;

        [Key]
        
public   string  TypeName
        {
            
get
            {
                
return  _TypeName;
            }
            
set
            {
                SetPropertyValue(
" TypeName " ref  _TypeName, value);
            }
        }

        
public   uint  CurrentID
        {
            
get
            {
                
return  _CurrentID;
            }
            
set
            {
                SetPropertyValue(
" CurrentID " ref  _CurrentID, value);
            }
        }

    }

 

简单试了一下,原来代码使用的那种反复重试的方式实际上比线程锁消耗的时间更多得多,这也好理解,一个线程锁消耗的时间比反复去数据库端尝试读写便宜多了。

 

但这代码实际上仍然不完美, 例如当我们要用这段代码为User,Product,Order等多个类生成ID时,都会受到同一个线程锁的影响,实际上这是不必要的,理想的情况下User管User锁,Product管Product锁就行了,以此类推。目前这段代码在我的项目中用得不多,这样还OK了就先不动了。或者您有好办法的话,不吝赐教啊。

转载于:https://www.cnblogs.com/Elvin/archive/2011/07/25/2116417.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值