23

Visual Studio 2008 C#.NET 3.5プロジェクトがあり、Fooオブジェクトのスレッドセーフなプールが必要です。

public class FooPool
{
    private object pool_lock_ = new object();
    private Dictionary<int, Foo> foo_pool_ = new Dictionary<int, Foo>();

    // ...

    public void Add(Foo f)
    {
        lock (pool_lock_)
        {
            foo_pool_.Add(SomeFooDescriminator, f);
        }
    }

    public Foo this[string key]
    {
        get { return foo_pool_[key]; }
        set { lock (pool_lock_) { foo_pool_[key] = value; } }
    }

    public IEnumerable<Foo> Foos
    {
        get
        {
            lock (pool_lock_)
            {
                // is this thread-safe?
                return foo_pool_.Select(x => x.Value);
            }
        }
    }
}

public IEnumerable<Foo> Foos { get; }関数はスレッドセーフですか?または、結果のクローンを作成して新しいリストを返す必要がありますか?

4

5 に答える 5

22

いいえ、そうではありません。

呼び出し元がそれを列挙しているときに別のスレッドが辞書に追加されると、エラーが発生します。

代わりに、次のことができます。

lock (pool_lock_) {
    return foo_pool.Values.ToList();
}
于 2012-04-19T16:35:50.187 に答える
19

IEnumerable<Foo> Foos { get; }関数はスレッドセーフですか?

いいえ。

または、結果を複製して新しいリストを返す必要がありますか?

いいえ、それも正しくないからです。間違った答えを返すスレッドセーフな方法はあまり役に立ちません。

ロックしてコピーを作成すると、返されるのは過去のスナップショットですロックが解除された瞬間にコレクションが完全に異なるものに変更される可能性があります。コピーを作成してこのスレッドセーフを作成すると、呼び出し元に嘘でいっぱいのバッグを渡すことになります。

シングルスレッド コードを扱っている場合、何かを変更するための特定の措置を講じない限り、すべてが同じままであるというのが妥当なモデルです。これは、マルチスレッド コードでは妥当なモデルではありません。マルチスレッド コードでは、反対のことを想定する必要があります。物事が変化していないことを確認するための特定の手段 (ロックなど) を講じない限り、すべてが常に変化しています。遠い過去、数百ナノ秒前の世界の状態を表す Foo のシーケンスを配布するメリットは何ですか? その時間で世界全体が変わる可能性があります。

于 2012-04-19T19:25:48.210 に答える
18

スレッドセーフではありません。あなたは戻る必要がありますToList()

return foo_pool_.Select(x => x.Value).ToList();

実行の延期に注意してください!

実際のコードは、ロックが終了した後に実行されます

// Don't do this
lock (pool_lock_)
{
    return foo_pool_.Select(x => x.Value); // This only prepares the statement, does not run it
}
于 2012-04-19T16:36:01.687 に答える
1

SynchronizedCollection、

SynchronizedCollection クラス ジェネリック パラメーターで指定された型のオブジェクトを要素として含む、スレッド セーフなコレクションを提供します。

http://msdn.microsoft.com/en-us/library/ms668265.aspx

于 2012-04-19T20:28:55.260 に答える
0

すべての読み取りアクセスをロックすると、パフォーマンスが非常に悪くなります。また、 toList を使用するための提案では、毎回メモリも割り当てます。

.NET 4 を使用している場合は、新しいスレッド セーフ コレクションの ConcurrentDictionary クラスを使用するだけです。それらは、複数のスレッドからデータにアクセスするための非常に高速な (ロックのない) メカニズムを提供します。

http://msdn.microsoft.com/en-us/library/dd997305.aspx

古い.NETバージョンを使用している場合は、foreachの代わりにcount変数を使用してサイクルを使用することをお勧めします。要素を削除せずに追加するだけで機能します(例のように)

于 2012-04-19T20:57:40.143 に答える