1

ConcurrentDictionary や ConcurrentQueue などの新しい .NET 同時実行コレクションのいくつかを知り、キューに並列に書き込むとどうなるかを確認するためにいくつかのテストを実行していました。

だから私はこれを実行しました:

    private static void ParallelWriteToQueue(Queue<int> queue)
    {
        Stopwatch sw = Stopwatch.StartNew();
        Parallel.For(1,1000001,(i) => queue.Enqueue(i));
        sw.Stop();
        Console.WriteLine("Regular int Queue - " + queue.Count + " time" + sw.ElapsedMilliseconds);
    }

そして、私が次の例外を得たと思った:

Source array was not long enough. Check srcIndex and length, and the array's lower bounds.

したがって、この Queue は予測どおりに同時エンキューを処理できません。

でも、キューの型を文字列に変えたら例外なく、結果は

Regular string Queue - 663209 time117

これは、約 663k だけがキューに入れられたことを意味します。

なぜ例外がなかったのですか?

エンキューされていないすべてのアイテムはどうなりましたか?

これは Queue と同じ機能です

    private static void ParallelWriteToQueue(Queue<string> queue)
    {
        Stopwatch sw = Stopwatch.StartNew();
        Parallel.For(1, 100001, (i) => queue.Enqueue(i.ToString()));
        sw.Stop();
        Console.WriteLine("Regular string Queue - " + queue.Count + " time" + +sw.ElapsedMilliseconds);
    }
4

4 に答える 4

4

Queue<T>とは対照的にConcurrentQueue<T>、MSDNによると、スレッドセーフではありません。Queue<T>説明する残りの動作は、純粋にスレッドセーフではないという事実に基づいて、同時(マルチスレッド)書き込みアクセスから発生する衝突から偶然に発生します。

于 2012-08-16T06:25:00.023 に答える
2

例外が発生するかどうかは、キューに入れるタイプとは関係ありません。それは非決定論的であり、両方のタイプで例外を再現でき、コードを変更することなく、両方のタイプでケースを例外なく再現できます。

次のスニペットを実行すると、これが示されます。

int exceptions = 0;
int noExceptions = 0;
for (int x = 0; x < 100; ++x)
{
    Queue<int> q = new Queue<int>();
    try
    {
        Parallel.For(1,1000001,(i) => q.Enqueue(i)); 
        ++noExceptions;
    }
    catch
    {
        ++exceptions;
    }
}

Console.WriteLine("Runs with exception: {0}. Runs without: {1}", exceptions, noExceptions);

出力は次のようなものですRuns with exception: 96. Runs without: 4

その理由は、他の人がすでに述べたように、Queueスレッドセーフではないためです。ここで起こることを「競合状態」と呼びます。

于 2012-08-16T06:28:57.370 に答える
2

あなたのテストが示すように、標準コレクションの実装はスレッドセーフではありません。文字列ではなく int で例外がスローされるという事実は、おそらく単なる偶然であり、テストを再試行すると、異なる結果が得られる可能性があります。

「失われた」アイテムに関しては、決定することはできません。マルチスレッド アクセスの結果、キューの内部状態が破損している可能性が高いため、カウント自体が間違っているか、アイテムが単にエンキューされていない可能性があります。

于 2012-08-16T06:30:24.420 に答える
1

を使用しているため、通常の作業を提供するParallel.For()には、コレクションがスレッドセーフである必要があります。

したがって、ConcurrentQueue<T>クラスの使用を検討してください。

于 2012-08-16T06:24:27.220 に答える