2

私はC#の並行辞書の実装に取り​​組んでいますが、このGetEnumerator()実装が実際に(スレッド)セーフであるかどうか疑問に思っています。

実際のスナップショットを実行していないので、後で内部ディクショナリへの読み取り/書き込みを台無しにするのか、それとも公開された列挙IEnumeratorが実際にロック内で実行されるために潜在的なデッドロックを公開するのか疑問に思います。

private readonly Dictionary<TKey, TValue> internalDictionary;
private SpinLock spinLock = new SpinLock();

IEnumerator IEnumerable.GetEnumerator()
{
    IEnumerator enumerator;

    bool lockTaken = false;
    try
    {
        spinLock.TryEnter(ref lockTaken);
        enumerator = (this.internalDictionary as IEnumerable).GetEnumerator();
    }
    finally
    {
        if (lockTaken)
        {
            spinLock.Exit(false);
        }
    }

    return enumerator;
}
4

2 に答える 2

2

あなたのメソッドは、並行ライターに関してスレッドセーフではありません。

  1. 列挙子は何もスナップショットを作成していません。元の辞書を参照しています。ToList実際にスナップショットを撮るためにそれか何かを呼び出します。
  2. 並行ライターはロックを使用しないため、同時に実行されます。これは安全ではありません。
  3. ロック本体が大きい場合は、スピンロックを使用しないでください。
  4. TryEnterが失敗した場合はどうなりますか?やっぱりトライといいます。

これは、すべての巧妙さを取り除いた修正バージョンです。

IEnumerator IEnumerable.GetEnumerator()
{
    lock (internalDictionary) return internalDictionary.ToList();
}

並行ライターもロックをかける必要があります。

于 2013-01-31T22:25:44.847 に答える
1

最初に頭に浮かぶのは、.NETにスレッドセーフな並行コレクションクラス(辞書を含む)が付属しているのに、いったいなぜこのような獣を自分で作成したいのかということです。詳細については、 GoogleSystem.Collections.Concurrentをご覧ください。

私は以前にConcurrentDictionaryクラスのベンチマークを行いましたが、より重いロックメカニズムを回避するためにスピンロックまたはインターロックを使用している場合でも、自分で作成できるものよりも大幅に高速であることを保証します。

あなたの質問に答えて、多分、しかしそれは実装に依存します(これは決して良いことではありません)。標準のコレクションクラスは反復中に変更できないため、書き込みの試行は失敗すると思います。しかし、私は自分のコードを安全に保つためにそのようなメカニズムに依存することはありません。

于 2013-01-31T21:52:05.280 に答える