我正在整理一个自定义的SynchronizedCollection T class,以便我可以为我的 WPF应用程序提供一个同步的Observable集合.同步是通过ReaderWriterLockSlim提供的,在大多数情况下,它很容易应用.我遇到问题
private class SynchronizedEnumerator : IEnumerator<T> { private SynchronizedCollection<T> _collection; private int _currentIndex; internal SynchronizedEnumerator(SynchronizedCollection<T> collection) { _collection = collection; _collection._lock.EnterReadLock(); _currentIndex = -1; } #region IEnumerator<T> Members public T Current { get; private set;} #endregion #region IDisposable Members public void Dispose() { var collection = _collection; if (collection != null) collection._lock.ExitReadLock(); _collection = null; } #endregion #region IEnumerator Members object System.Collections.IEnumerator.Current { get { return Current; } } public bool MoveNext() { var collection = _collection; if (collection == null) throw new ObjectDisposedException("SynchronizedEnumerator"); _currentIndex++; if (_currentIndex >= collection.Count) { Current = default(T); return false; } Current = collection[_currentIndex]; return true; } public void Reset() { if (_collection == null) throw new ObjectDisposedException("SynchronizedEnumerator"); _currentIndex = -1; Current = default(T); } #endregion }
但是,我担心的是,如果Enumerator没有Disposed,那么锁永远不会被释放.在大多数用例中,这不是问题,因为foreach应该正确调用Dispose.但是,如果使用者检索显式的Enumerator实例,则可能会出现问题.我唯一的选择是使用一个警告实现者来记录该类,提醒消费者在明确使用Enumerator时调用Dispose,或者有没有办法在最终确定期间安全地释放锁定?我想不是,因为终结器甚至没有在同一个线程上运行,但我很好奇是否还有其他方法来改进它.
编辑
在考虑了这一点并阅读了回复之后(特别感谢Hans),我认为这绝对是一个坏主意.实际上,最大的问题不是忘记Dispose,而是一个悠闲的消费者在枚举时创造僵局.我现在只读取锁定足够长的时间来获取副本并返回副本的枚举器.
你是对的,这是一个问题.终结器是没用的,它运行得太晚,无法使用.无论如何,代码应该在此之前严重陷入僵局.不幸的是,您无法区分调用MoveNext / Current成员的foreach或显式使用它们的客户端代码.没有修复,不要这样做.微软也没有这样做,他们有充分的理由支持.NET 1.x.您可以创建的唯一真正的线程安全迭代器是在GetEnumerator()方法中创建集合对象的副本的迭代器.迭代器与集合不同步也不是快乐.