処理が必要なアイテムをエンキューするインデクサーを作成しています。インデクサーは、アイテムをそのプロセッサーに追加します。たとえば、100 個のアイテムを追加し、その後 3 分間アイテムを追加せず、さらに 50 個のアイテムを追加します。
public class Processer
{
private ConcurrentQueue<Item> items;
public void AddItem(Item item)
{
this.items.Enqueue(item);
}
}
アイテムはランダムな間隔で入ってくるので、これらのアイテムをデキューして処理する別のスレッドを作成します。
使用するのに最適なオプションは何ですか?
Collection を使用せず、ThreadPool を使用します。
public void AddItem(Item item) { ThreadPool.QueueUserWorkItem(function, item); }
これにより、キューが自動的に作成され、アイテムが処理されますが、20 個のアイテムが見つかると、インデクサーの実行がほぼ停止し、最初にこのスレッド プールが終了します。
長時間実行されるタスクを使用します。
public Processer() { this.task = Task.Factory.StartNew(() => DequeueItems(), CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); } public DequeueItems() { while(true) { Item item = null; while(this.items.TryDequeue(out item) { this.store.ExecuteIndex((AbstractIndexCreationTask)item); } Thread.Sleep(100); } }
しかし、私は while() と thread.sleep を使用しなければなりません。これは、enumerable がしばらくすると枯渇し、新しいアイテムがあるかどうかを再確認する必要があるためです。
実行時間の短いタスクを使用します。
public Processer() { } private void Run() { this.task = Task.Factory.StartNew(() => DequeueItems(), CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default); } public void AddItem(Item item) { this.items.Add(item); if(this.task == null || this.task.isCompleted) this.Run(); } public DequeueItems() { Item item = null; while(this.items.TryDequeue(out item) { this.store.ExecuteIndex((AbstractIndexCreationTask)item); } }
こっちの方がいいかも?しかし、スレッドの開始は「コストのかかる」操作であり、IsCompleted をチェックしているのでアイテムを見逃す可能性があるかどうかはわかりません。これは while ループを終了する過程にあり、このようにして 1 つのアイテムが失われる可能性があります。しかし、それはスリープせず、汚い while ループを使用します。
MSDN は TPL を使用することを推奨しているため、Threads を使用しないことを考えましたが、この問題を処理するためのより良い方法があるかもしれません。
変更ログ
- BlockingCollection に変更
- ConcurrentQueue に戻しました
私がチェックしたいくつかのこと:
- ワーカー スレッドを使用してアイテムをデキューする(ThreadPool を使用)
- ダミーのスレッド キュー(TPL ではなくスレッド ソリューションのみを使用)
- ThreadPool.QueueUserWorkItem の予期しない動作(上記と同じ)
- C# -マルチスレッド コードを使用する場合は、TPL が優先されることを示すThreadPool と Tasks の比較