3

私は.NET 4.5 Single Instance WCFサービスを持っています。これはリスト内のアイテムのコレクションを同時に保持し、同時にリーダーとライターを持っていますが、ライターよりもはるかに多くのリーダーを持っています。

現在、BCL を使用するかConcurrentBag<T>、独自のカスタム汎用ThreadSafeListクラスを使用するかを決定しています (BCL を拡張IList<T>およびカプセル化するのReaderWriterLockSlimは、複数の同時読み取りにより適しているためです)。

100 万人のリーダー (Sum Linq クエリを実行するだけ) と 100 人のライター (リストに項目を追加する) の同時シナリオをシミュレートして、これらの実装をテストしたところ、多数のパフォーマンスの違いが見つかりました。

私のパフォーマンス テストでは、タスクのリストがあります。

List<Task> tasks = new List<Task>();

テスト 1: 次のコードを使用して、100 万個のリーダー タスクに続いて 100 個のライター タスクを作成した場合:

tasks.AddRange(Enumerable.Range(0, 1000000).Select(n => new Task(() => { temp.Where(t => t < 1000).Sum(); })).ToArray());
tasks.AddRange(Enumerable.Range(0, 100).Select(n => new Task(() => { temp.Add(n); })).ToArray());

次のタイミング結果が得られます。

  • コンカレントバッグ: ~300ms
  • スレッドセーフリスト: ~520ms

テスト 2: ただし、100 個のライター タスクと混合された 100 万のリーダー タスクを作成すると (これにより、実行されるタスクのリストは {Reader,Reader,Writer,Reader,Reader,Writer etc} になります。

foreach (var item in Enumerable.Range(0, 1000000))
{
    tasks.Add(new Task(() => temp.Where(t => t < 1000).Sum()));
    if (item % 10000 == 0)
        tasks.Add(new Task(() => temp.Add(item)));
}

次のタイミング結果が得られます。

  • コンカレントバッグ: ~4000ms
  • スレッドセーフリスト: ~800ms

各テストの実行時間を取得するための私のコードは次のとおりです。

Stopwatch watch = new Stopwatch();
watch.Start();
tasks.ForEach(task => task.Start());
Task.WaitAll(tasks.ToArray());
watch.Stop();
Console.WriteLine("Time: {0}ms", watch.Elapsed.TotalMilliseconds);

カスタム実装よりも、テスト 1 の ConcurrentBag の効率が良く、テスト 2 の ConcurrentBag の効率が悪いため、どちらを使用するかを判断するのは難しいと思います。

Q1. リスト内のタスクの順序だけを変更しているのに、結果がこれほど異なるのはなぜですか?

Q2. テストをより公平にするためにテストを変更するより良い方法はありますか?

4

1 に答える 1

2

リスト内のタスクの順序だけを変更しているのに、結果がこれほど異なるのはなぜですか?

私の最善の推測は、読み取るものが何もないため、Test #1実際にはアイテムを読み取らないということです。タスクの実行順序は次のとおりです。

  1. 共有プールから 1M 回読み取り、合計を計算する
  2. 共有プールに 100 回書き込む

読み取りTest # 2と書き込みが混在しているため、異なる結果が得られると思います。

テストをより公平にするためにテストを変更するより良い方法はありますか?

タスクを開始する前に、タスクの順序をランダム化してみてください。同じ結果を再現するのは難しいかもしれませんが、実際の使用に近づくかもしれません。

最終的に、あなたの質問は楽観的並行性(Concurrent*クラス)と悲観的並行性(ロックに基づく)の違いについてです。経験則として、共有リソースへの同時アクセスの可能性が低い場合は、楽観的同時実行を優先します。同時アクセスの可能性が高い場合は、悲観的なアクセスを優先します。

于 2015-05-29T13:58:04.060 に答える