之前写了一篇《对不能用using的成对操作,快速扩展IDisposable的方法》提到了如何快速的把销毁操作用闭包的形式封装为IDisposable,并且实现了一个ReaderWriteerLockSlimHelper。

 

对于没有using的RWLock似乎已经很好用了, 但是我仍嫌弃其不够简单。

 

资源读写锁的应用,绝大多数是针对某批特定的资源。如果为这样的资源做一个封装,资源的引用需要被隐藏在封装内,封装外不通过读锁不可以访问资源。显然ReaderWriteerLockSlimHelper却无法做到这一点。

 

我们在上文中已经建立了一个 Disposable对象,取得该对象意味着我们可以加锁,将其销毁我们就可以解锁,其生存期与整个加锁周期吻合。对于读写锁来说具有这种行为的对象是一种作为读写钥匙/令牌的存在。解锁后的资源引用放在这个令牌上作为属性暴露出来再合适不过了。

以此为考量,我们先设计一个实现IDisposable,读写锁的令牌

/// <summary>
/// 读写资源的令牌
/// </summary>
public abstract class LockToken<T> : IDisposable
{
protected LockableObjectContainer<T> _container;

/// <summary>
/// 被锁住的资源引用
/// </summary>
public T Value { get; set; }
/// <summary>
/// 读写锁的原钥匙,对其销毁即为解锁
/// </summary>
protected IDisposable _innerTicket;


/// <summary>
/// 创建读写资源的令牌
/// </summary>
/// <param name="value">解锁的资源引用</param>
/// <param name="innerTicket">锁的真实引用</param>
/// <param name="beforeDispose">在解锁前需要做的操作 如把资源的新值覆盖回锁住的资源</param>
/// <param name="afterDispose">在解锁后需要做的操作 如写日志</param>
internal LockToken(LockableObjectContainer<T> container, IDisposable innerTicket, Action<LockToken<T>> beforeDispose, Action<LockToken<T>> afterDispose)
{
_container=container;
_innerTicket
= innerTicket;
Value
= container._value;
this._disposeAction =
()
=>
{
try
{

if (beforeDispose != null) beforeDispose(this);
this.Value = default(T);
_innerTicket.Dispose();
if (afterDispose != null) afterDispose(this);
}
catch
{


}
};
}

#region IDisposable Members

/// <summary>
/// 令牌销毁时要做的操作
/// </summary>
Action _disposeAction;

/// <summary>
/// 销毁
/// </summary>
public void Dispose()
{
_disposeAction();
}

#endregion
}

读写时,所做的操作略有不同。写令牌在销毁时要把令牌上的新引用/值 覆盖到容器内

依此我们可以做两个不同的令牌子类

 

class ReadLockToken<T> : LockToken<T>
{
public ReadLockToken(LockableObjectContainer<T> container, ReaderWriterLockSlim _lock) :
base(container, _lock.CreateLockScope(LockType.Read), null, null)
{
}

}



class WriteLockToken<T> : LockToken<T>
{
public WriteLockToken(LockableObjectContainer<T> container, ReaderWriterLockSlim _lock)
:
base
(
container,
_lock.CreateLockScope(LockType.Write),
lt
=>
{
//在解锁前 把新值保存给原对象
lt._container._value =lt.Value;
           },
          null
)
{
}

}

这些做好后,编写容器就很容易了

 

 

 

public class LockableObjectContainer<T>
{
internal protected T _value;


System.Threading.ReaderWriterLockSlim _lock
= new
System.Threading.ReaderWriterLockSlim();
public
LockableObjectContainer(T value)
{
_value
=
value;

}

public LockToken<T>
GetReadToken()
{
return new ReadLockToken<T>(this
, _lock);
}
public LockToken<T>
GetWriteToken()
{
return new WriteLockToken<T>(this
, _lock);

}

}

 

 

上面的内容很枯燥

写读写锁的时候很方便

 

static LockableObjectContainer <Dictionary <Type ,IFactoryContainer > > planConnectionGetters
= new LockableObjectContainer<Dictionary<Type, IFactoryContainer>>
(
new Dictionary<Type, IFactoryContainer>());


。。。。。。。。。。。。。。。。。。。。。。。。。



using (var token =planConnectionGetters.GetReadToken())
{
var dic
= token.Value;

if (dic.TryGetValue(contractType , out channelFactory ))
{
return channelFactory.GetChannel (uri);

}

}


using (var token = planConnectionGetters.GetWriteToken ())
{
var dic
= token.Value;

if (!dic.TryGetValue(contractType, out channelFactory))
{
Type t
= typeof(FactoryContainer<>);
channelFactory
= Activator.CreateInstance(t.MakeGenericType(contractType)) as IFactoryContainer;
dic.Add(contractType, channelFactory);

}



}
 如果容器经常保存字典等常用集合对象 我们也可以这样做一些扩展方法

public static TValue GetOrCreateValue<TKey, TValue>
(
this LockableObjectContainer<IDictionary<TKey, TValue>> container,
TKey key,
Predicate
<TValue > valueChecker,
Func
<TValue> valueFactory
)
{
TValue val
=default (TValue);
using (var token = container.GetReadToken())
{

var dic
= token.Value;
if (dic.TryGetValue(key, out val))
{
if (valueChecker (val))
return val;

}
}

using (var token = container.GetWriteToken())
{
var dic
= token.Value;
if (dic.TryGetValue(key, out val))
{
if (valueChecker(val))
return val;

}
val
=valueFactory();
dic.Add(key,val) ;

}
return val;

}




------------------------------------------------------------

TQueue q;
q
= instanceQueues.GetOrCreateValue(instance, _ => true, () => new TQueue());
return q;

posted @ 2011-03-24 14:57 韦恩卑鄙 a-zhewg @waynebaby 阅读(1180) 评论(3) 编辑

日常操作中有很多需要打开/关闭   加锁/解锁的成对操作

有时候一些操作原生支持  IDisposable
Monitor 可以用Lock(){}   但是ReadWriteLock 就难了。 还有WCF Channel等)。
这种情况就要用  try/catch/finally,很是丑

封装成IDisposable可能很烦,因为多一个对象要多好多文档。

虽然AOP可能解决一些问题, 但是又没办法精确定位  scrope. 
还是 IDisposable +using最爽

 

所以写了一个缺省实现。

    /// <summary>  

    /// 销毁帮手,生成可以支持using的自定义IDisposable实例
    /// <remarks>感谢网友@doggo对于 +=OnDispose功能的測試,由於不完善這裡決定取消該功能</remarks>

    /// </summary>  

    public struct Disposable : IDisposable
    {


   
        /// <summary>  

        /// 创建销毁帮手实例  

        /// </summary>  

        /// <param name="onCreate">创建时要做的操作</param>  

        /// <param name="onDispose">销毁是要做的操作</param>  

        public Disposable(Action onCreate, Action onDispose)
        {
            OnDispose = onDispose;


            onCreate();

        }



        /// <summary>  

        /// 销毁时要做的操作          
       /// </summary>  

        private Action OnDispose
        {

                get ;set;

             }  

        ////// <summary>  

        ////// 销毁时要做的操作  支持+=/Addhandler附加操作 (撤銷)  

        ////// </summary>  

        //////public event Action OnDispose ;


        #region IDisposable 成员



        void IDisposable.Dispose()
        {

        
            OnDispose();

            OnDispose = null;

        }



        #endregion



    } 

 

 

思路是用一个扩展方法,给一个无dispose 能力的对象 建立一个IDisposable的引用。

由于onCreate onDispose是闭包 额外的参数也是非必要的。

 

这里提供一个简易读写锁的实现。  大家可以参考

 

 

    public static class ReaderWriteerLockSlimHelper 
    {
        /// <summary>
        /// 为读写锁创建支持using的IDisposable帮手
        /// </summary>
        /// <param name="instance">读写锁实例</param>
        /// <param name="lockType">加锁类型 读/写</param>
        /// <returns>帮手实例</returns>
        public static IDisposable CreateDisposable(this ReaderWriterLockSlim instance, LockType lockType)
        {
            var kvp = LockDisposeDic[lockType];
            return new Disposable(() => kvp.Key(instance), () => kvp.Value(instance));
        }

        /// <summary>
        /// 读写的不同操作字典
        /// </summary>
        static Dictionary<LockType, KeyValuePair<Action<ReaderWriterLockSlim>, Action<ReaderWriterLockSlim>>> LockDisposeDic = new Dictionary<LockType, KeyValuePair<Action<ReaderWriterLockSlim>, Action<ReaderWriterLockSlim>>>()
        {
            {
                LockType.Read, 
                new KeyValuePair<Action<ReaderWriterLockSlim>,Action<ReaderWriterLockSlim>> 
                    (
                        ins=>ins.EnterReadLock(),
                        ins=>ins.ExitReadLock()
                    ) 
               
            },
            {
                LockType.Write, 
                new KeyValuePair<Action<ReaderWriterLockSlim>,Action<ReaderWriterLockSlim>> 
                    (
                        ins=>ins.EnterWriteLock(),
                        ins=>ins.ExitWriteLock()
                    ) 
               
            }
        };
            
    }

    public enum LockType
    {
        Read,
        Write
    }

 

 

实际使用起来就是爽。 这是一个在需要并发访问的队列中 加入对象的方法。

 

 

/// <summary>
        /// 加入有序队列。
        /// </summary>
        /// <param name="item">加入的项目</param>
        public void Enqueue(TValue item)
        {
            using (_lock.CreateDisposable(LockType.Write))
            {
                Queue<TValue> enqueueTarget;
                var key=_keySelector(item);
                if (!_items.ContainsKey(key))
                {
                    var sortValue = _sortValueSelector(item);
                    if (!_index.TryGetValue( _sortValueSelector(item) ,out enqueueTarget ))
                    {
                        enqueueTarget = new Queue<TValue>();
                        _index.Add(sortValue, enqueueTarget);
                    }

                    enqueueTarget.Enqueue(item);
                    _items.Add(key, item);
                }
                else
                {
                    throw new InvalidOperationException("this Item already in queue");
                }
            }

        }

 

个人工作分享。希望能够帮助大家提高工作效率

 

附: 容易漏调结束行为的实现  和try catch finally 实现  大家比较下

  

 

   出异常会死锁的错误成对掉用 

        /// <summary>
        /// 加入有序队列。
        /// </summary>
        /// <param name="item">加入的项目</param>
        public void Enqueue1(TValue item)
        {
            _lock.EnterWriteLock();

    
                Queue<TValue> enqueueTarget;
                var key = _keySelector(item);
                if (!_items.ContainsKey(key))
                {
                    var sortValue = _sortValueSelector(item);
                    if (!_index.TryGetValue(_sortValueSelector(item), out enqueueTarget))
                    {
                        enqueueTarget = new Queue<TValue>();
                        _index.Add(sortValue, enqueueTarget);
                    }

                    enqueueTarget.Enqueue(item);
                    _items.Add(key, item);
                }
                else
                {
                    throw new InvalidOperationException("this Item already in queue");
                }
                _lock.ExitReadLock(); //不但可能中途退出 还可能像这样写错解锁方法


        }

 

   正确而麻烦的try catch finally

        /// <summary>
        /// 加入有序队列。
        /// </summary>
        /// <param name="item">加入的项目</param>
        public void Enqueue2(TValue item)
        {
            _lock.EnterWriteLock();

            try
            {
                Queue<TValue> enqueueTarget;
                var key = _keySelector(item);
                if (!_items.ContainsKey(key))
                {
                    var sortValue = _sortValueSelector(item);
                    if (!_index.TryGetValue(_sortValueSelector(item), out enqueueTarget))
                    {
                        enqueueTarget = new Queue<TValue>();
                        _index.Add(sortValue, enqueueTarget);
                    }

                    enqueueTarget.Enqueue(item);
                    _items.Add(key, item);
                }
                else
                {
                    throw new InvalidOperationException("this Item already in queue");
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                _lock.ExitWriteLock();
            
            }


        }

posted @ 2010-12-09 11:13 韦恩卑鄙 a-zhewg @waynebaby 阅读(1867) 评论(23) 编辑

*貌似是炫耀博?*

卑鄙本周新入一台  inter i7 平台 gts250 12g内存的主机  自己折腾用。

win7 评分

cpu 内存7.5

显示7.9

硬盘5.8

----------出问题了----------------

仆自以为很爽 ,但是杯子和牙刷总是伴随存在的。

不想在安装win7 64后出现 最大化最小化停靠等aero动画速度缓慢的症状。

仔细观察 是把原来半秒左右的动画 逐桢以慢动作的方式延长到了一秒多。

后来开发wpf程序练习,发现checkbox标准动画 从unchecked状态到checked状态动画也要1秒多。心想这下坏了,wpf要废?

马上找一个silverlight 应用 cool.pptv.com 发现也是动画奇慢。

 

---------用经验解决-----------------------

经观察wpf和sl的动画慢归满  动作却非常平滑,毫无丢桢。8个cpu监视窗也在5%以下。

看起来是时钟频率有些问题。

sl wpf 和aero 一定是参考了一个不正确的时钟  导致播放动画的用时计算出错。

只因为aero是定桢动画,而wpf于sl是dx加速的即时生成 才有显示上的差别。

 

i7太新了,有些功能win7还不能正确的支持吧?

google 10分钟无解。还是得自己想办法。

 

打开bios 查看可疑的项目逐个设置观察,在cell设置中 cpu找到一个intel turbo boost的功能。

turbo让我想起儿时486sx 33。这个机器在前面版上有个turbo按键。按下去,面板上的频率数字就会从16变成33 khz,有时候在16khz状态下打开的dos游戏,如果按下turbo,就会突然变快。这招玩韩国街霸的时候很有用。

恩。。。似乎是和时钟频率相关。

关掉

f10

保存。

 

重新进入系统 sl/wpf/aero 

all pass.

 

后来问硬件达人,说这是个叫做“睿频”的技术  在空闲时候自动降频,在需要的时候还可能有超频。

我想,这个技术也许不错,物尽其用嘛。但用它跑时间敏感的应用,比如秒表啊,性能计数啊,网络游戏啊 blabla..,我是不大敢。还是关上保险点。

 

鉴于之前google无果,特发于博上,希望后来者能够参考。

希望win7 x64 和intel 早点互通有无 随时把最新的时钟频率更新给用户态。 

  

posted @ 2010-10-26 22:55 韦恩卑鄙 a-zhewg @waynebaby 阅读(501) 评论(6) 编辑
        public virtual bool CanExecute(object parameter)
        {
            bool hasEnabledCommandsThatShouldBeExecuted = false;

            ICommand[] commandList;
            lock (this.registeredCommands)
            {
                commandList = this.registeredCommands.ToArray();
            }
            foreach (ICommand command in commandList)
            {
                if (this.ShouldExecute(command))
                {
                    if (!command.CanExecute(parameter))
                    {
                        return false;
                    }

                    hasEnabledCommandsThatShouldBeExecuted = true;
                }
            }

            return hasEnabledCommandsThatShouldBeExecuted;
        }

posted @ 2010-02-20 14:01 韦恩卑鄙 a-zhewg @waynebaby 阅读(503) 评论(0) 编辑

posted @ 2009-10-28 20:36 韦恩卑鄙 a-zhewg @waynebaby 阅读(742) 评论(7) 编辑
摘要: 没错,这次章节没有女仆。 前情回顾 我们的最初的需求是建立一个拉模式下用户暂存的顺序信息池 还是这张工作模式图 我们可以把这个需求设计为 Clear:清除所有内容GetEnumerator :实现枚举器,新向旧方向的顺序枚举,这样一旦到达上次读取的时间就可以中断枚举。RecycleFromButtom:从旧向前进行搜索 把满足条件的扔到GCStackOn :把一个新信息放在堆栈的顶部 这就好像是...阅读全文
posted @ 2009-08-27 14:06 韦恩卑鄙 a-zhewg @waynebaby 阅读(1429) 评论(2) 编辑
摘要:   老少爷们儿反击战 上一篇中 我们的女仆终于可以做一些像阳光下其他人一样的事情了,少爷们可以和女仆酱一起参加下午茶~ 难得的上流社会啊 这是永远1v1被人私有的女奴 和 喝茶时被人共有的女仆酱最明显的差异~ 明媚的午后阳光下,庭院里白色长餐桌两旁,英俊的少爷们彼此交换着最近的趣闻轶事,一面欣赏女仆酱以1/3几率打翻茶水,可爱而笨笨地努力侍奉着,闪闪发光的样子。 人间极乐喵~...阅读全文
posted @ 2009-08-22 22:59 韦恩卑鄙 a-zhewg @waynebaby 阅读(1531) 评论(6) 编辑
摘要: 上一篇我们大致的了解了几种聊天室的行为模式 最简单明了的推模式 几乎不需要任何多余的语言来描述它的实现 这一篇我们看看如何实现拉模式更有效。 本图清晰的表现了"拉"模式聊天室的行为。 并发多用户向数据池写数据 并发多用户从数据池读书据 数据最好以时间为顺序储存在集合中 某时间向后的枚举查找将是最大的消耗。 聊天室进化 -女仆编年史神秘的原始社会 仍然参考我们神奇朴素的Asp3聊天室 53 Appl...阅读全文
posted @ 2009-08-21 23:40 韦恩卑鄙 a-zhewg @waynebaby 阅读(1984) 评论(6) 编辑
摘要: 距离上次写大厅已经有几个月。中间工作繁忙,待学习的东西又很多,又有很多新产品的想法想实践,一直把这个坑闲置着。直到前两天简单的树遍历枚举器-挑战一个程序员到底能多懒 装配脑袋的几个回帖给我带来一些灵感,让我又重新看看我聊天频道的实现,发现竟然有很多可以推敲重新处理的地方。昨天上班的时候又把聊天部分整理了下,愿和大家分享。Wiki定义的聊天室网络聊天室通常直称聊天室,是一种人们可以在线交谈的的网络论...阅读全文
posted @ 2009-08-19 20:41 韦恩卑鄙 a-zhewg @waynebaby 阅读(2801) 评论(11) 编辑
摘要: 前一阵在递归算法相关回贴的讨论中 和某lz抱怨 现在的同志们连用自己的栈加循环模拟递归都不会做了。如果自己实现递归栈 又怎么会在线程栈中储存过多无关信息?数据全部都在堆里 又怎会stackoverflow?当时就有想法自己实现一个,造福一下群众,但是被坏心眼的某lz 阻止了。“让他们自己写。” 他大概是这么说滴,“他们自己写了才算懂得了。”可是 最近...阅读全文
posted @ 2009-08-16 01:53 韦恩卑鄙 a-zhewg @waynebaby 阅读(3003) 评论(41) 编辑