[转载]对资源加读写锁的容器 – 怕怕的韦恩卑鄙 – 博客园.
之前写了一篇《对不能用using的成对操作,快速扩展IDisposable的方法》提到了如何快速的把销毁操作用闭包的形式封装为IDisposable,并且实现了一个ReaderWriteerLockSlimHelper。
对于没有using的RWLock似乎已经很好用了, 但是我仍嫌弃其不够简单。
资源读写锁的应用,绝大多数是针对某批特定的资源。如果为这样的资源做一个封装,资源的引用需要被隐藏在封装内,封装外不通过读锁不可以访问资源。
显然ReaderWriteerLockSlimHelper却无法做到这一点。
我们在上文中已经建立了一个 Disposable对象,取得该对象意味着我们可以加锁,将其销毁我们就可以解锁,其生存期与整个加锁周期吻合。对于读写锁来说具有这种行为的对象是一种作为读写钥匙/令牌的存在。解锁后的资源引用放在这个令牌上作为属性暴露出来再合适不过了。
以此为考量,我们先设计一个实现IDisposable,读写锁的令牌
/// /// 读写资源的令牌 /// public abstract class LockToken : IDisposable { protected LockableObjectContainer _container; /// /// 被锁住的资源引用 /// public T Value { get; set; } /// /// 读写锁的原钥匙,对其销毁即为解锁 /// protected IDisposable _innerTicket; /// /// 创建读写资源的令牌 /// /// <span> </span>解锁的资源引用 /// <span> </span>锁的真实引用 /// <span> </span>在解锁前需要做的操作 如把资源的新值覆盖回锁住的资源 /// <span> </span>在解锁后需要做的操作 如写日志 internal LockToken(LockableObjectContainer container, IDisposable innerTicket, Action> beforeDispose, Action> 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 /// /// 令牌销毁时要做的操作 /// Action _disposeAction; /// /// 销毁 /// 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;