カスタムSynchronizedCollection<T>
クラスをまとめて、WPF アプリケーション用に同期された Observable コレクションを作成できるようにしています。同期は、ほとんどの場合、簡単に適用できる ReaderWriterLockSlim を介して提供されます。私が問題を抱えているのは、コレクションのスレッドセーフな列挙を提供する方法です。次のようなカスタムのIEnumerator<T>
ネストされたクラスを作成しました。
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 を呼び出すことを思い出させる警告実装者でクラスを文書化する唯一のオプションはありますか? ファイナライザーは同じスレッド上でさえ実行されないため、私はそうは考えていませんが、これを改善する他の方法があるかどうか興味がありました.
編集
これについて少し考え、回答を読んだ後 (特にハンスに感謝)、これは間違いなく悪い考えだと判断しました。最大の問題は、実際には Dispose を忘れていることではなく、のんびりとした消費者が列挙中にデッドロックを作成していることです。コピーを取得し、コピーの列挙子を返すのに十分な時間だけ読み取りロックします。