There are many pairs of operations that need to be opened / closed, locked / unlocked in daily operation.
Sometimes some operations are natively supported by IDisposable.
Monitor can use Lock() {} but ReadWriteLock is difficult. And WCF Channel).
In this case, try/catch/finally. It's ugly.
It can be annoying to encapsulate IDisposable, because more objects require more documents.
Although AOP may solve some problems, it can't precisely locate the profile.
Or IDisposable +using
So a default implementation is written.
/// <summary> ///Destroy the helper to generate a user-defined IDisposable instance that can support using ///< remarks > thank you @ doggo for testing the + = OnDispose function. Due to the imperfection, we decided to cancel the function < / remarks > /// </summary> public struct Disposable : IDisposable { /// <summary> ///Create destroy helper instance /// </summary> ///< param name = "oncreate" > what to do when creating < / param > ///< param name = "ondispose" > destroy is the operation to be done < / param > public Disposable(Action onCreate, Action onDispose) { OnDispose = onDispose; onCreate(); } /// <summary> ///What to do when destroying
/// </summary> private Action OnDispose { get ;set; } ////// <summary> //////The operation to be done when destroying supports + = / Addhandler additional operation (undo) ////// </summary> //////public event Action OnDispose ; #region IDisposable member void IDisposable.Dispose() { OnDispose(); OnDispose = null; } #endregion }
The idea is to build an IDisposable reference to an object without dispose capability by using an extension method.
Because onCreate onDispose is a closure extra parameter is also unnecessary.
A simple read-write lock is provided here. You can refer to
public static class ReaderWriteerLockSlimHelper { /// <summary> ///Creating a using enabled IDisposable helper for read-write locks /// </summary> ///< param name = "instance" > read / write lock instance < / param > ///< param name = "locktype" > lock type read / write < / param > ///< returns > helper instance < / returns > public static IDisposable CreateDisposable(this ReaderWriterLockSlim instance, LockType lockType) { var kvp = LockDisposeDic[lockType]; return new Disposable(() => kvp.Key(instance), () => kvp.Value(instance)); } /// <summary> ///Different operation dictionaries for reading and writing /// </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 }
It's really cool to use. This is a way to add objects to queues that require concurrent access.
/// <summary> ///Join an ordered queue. /// </summary> ///< param name = "item" > added items < / 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"); } } }
Personal work sharing. Hope to help you improve your work efficiency
Attachment: implementation of easy to miss ending behavior and try catch finally implementation
Error of deadlock in case of exception
/// <summary> ///Join an ordered queue. /// </summary> ///< param name = "item" > added items < / 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(); //It's not only possible to quit midway, but also possible to write wrong unlocking methods like this. }
try catch finally
/// <summary> ///Join an ordered queue. /// </summary> ///< param name = "item" > added items < / 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(); } }
Reproduced in: https://www.cnblogs.com/waynebaby/archive/2010/12/09/1900998.html