2

System.Collections.Generic.SynchronizedCollection 共有コレクションがあります。私たちのコードは、.Net 4.0 タスク ライブラリを使用して複数のスレッドにまたがり、同期されたコレクションをスレッドに渡します。これまでのところ、スレッドはコレクションにアイテムを追加または削除していません。しかし、スレッドの 1 つを必要とする新しい要件では、コレクションからアイテムを削除する必要があり、もう 1 つのスレッドはコレクションを読み取るだけです。コレクションからアイテムを削除する前にロックを追加する必要がありますか? もしそうなら、リーダースレッドはスレッドセーフでしょうか? または、スレッドセーフを取得するための最良の方法を提案しますか?

4

3 に答える 3

8

いいえ、完全にスレッドセーフではありません。単純なコンソール アプリケーションで次のことを試して、例外でどのようにクラッシュするかを確認してください。

var collection = new SynchronizedCollection<int>();

var n = 0;

Task.Run(
    () =>
        {
            while (true)
            {
                collection.Add(n++);
                Thread.Sleep(5);
            }
        });

Task.Run(
    () =>
        {
            while (true)
            {
                Console.WriteLine("Elements in collection: " + collection.Count);

                var x = 0;
                if (collection.Count % 100 == 0)
                {
                    foreach (var i in collection)
                    {
                        Console.WriteLine("They are: " + i);
                        x++;
                        if (x == 100)
                        {
                            break;
                        }

                    }
                }
            }
        });

Console.ReadKey();

ここに画像の説明を入力

SynchronizedCollection を ConcurrentBag に置き換えると、スレッドセーフになることに注意してください。

var collection = new ConcurrentBag<int>();

SynchronizedCollection は、このアプリケーションでは単純にスレッドセーフではありません。代わりに並行コレクションを使用してください。

于 2016-03-24T18:12:31.803 に答える
7

アレクサンダーがすでに指摘しているようにSynchronizedCollection、このシナリオではスレッドセーフではありません。はSynchronizedCollection実際には通常の汎用リストをラップし、すべての呼び出しを基になるリストに委任し、呼び出しをロックします。これも で行われGetEnumeratorます。したがって、列挙子の取得は同期されますが、実際の列挙は同期されません。

var collection = new SynchronizedCollection<string>();
collection.Add("Test1");
collection.Add("Test2");
collection.Add("Test3");
collection.Add("Test4");

var enumerator = collection.GetEnumerator();
enumerator.MoveNext();
collection.Add("Test5");
//The next call will throw a InvalidOperationException ("Collection was modified")
enumerator.MoveNext();

foreach を使用する場合、列挙子はこの方法で呼び出されます。したがって、この配列を列挙する前に a を追加してToArray()も、これは最初にこの配列に列挙されるため、機能しません。この列挙は、 foreach 内で実行しているときの方が高速になる可能性があるため、同時実行の問題が発生する可能性を減らすことができます。Richard が指摘したように、真のスレッド セーフを実現するには、System.Collections.Concurrentクラスを使用する必要があります。

于 2016-04-21T09:24:15.300 に答える
2

はい、SynchronizedCollection がロックを行います。

リーダーが複数あり、ライターが 1 つしかない場合は、SynchronizedCollection の代わりに ReaderWriterLock の使用を検討することをお勧めします。

また、.Net 4+ の場合は、 をご覧くださいSystem.Collections.Concurrent。これらのクラスは、SynchronizedCollection よりもパフォーマンスがはるかに優れています。

于 2012-11-25T05:44:36.877 に答える