3

処理が必要なアイテムをエンキューするインデクサーを作成しています。インデクサーは、アイテムをそのプロセッサーに追加します。たとえば、100 個のアイテムを追加し、その後 3 分間アイテムを追加せず、さらに 50 個のアイテムを追加します。

public class Processer
{
    private ConcurrentQueue<Item> items;

    public void AddItem(Item item)
    {
        this.items.Enqueue(item);
    }
}

アイテムはランダムな間隔で入ってくるので、これらのアイテムをデキューして処理する別のスレッドを作成します。

使用するのに最適なオプションは何ですか?

  1. Collection を使用せず、ThreadPool を使用します。

    public void AddItem(Item item)
    {
        ThreadPool.QueueUserWorkItem(function, item);
    }
    

    これにより、キューが自動的に作成され、アイテムが処理されますが、20 個のアイテムが見つかると、インデクサーの実行がほぼ停止し、最初にこのスレッド プールが終了します。

  2. 長時間実行されるタスクを使用します。

    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 がしばらくすると枯渇し、新しいアイテムがあるかどうかを再確認する必要があるためです。

  3. 実行時間の短いタスクを使用します。

    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 ループを使用します。

  4. MSDN は TPL を使用することを推奨しているため、Threads を使用しないことを考えましたが、この問題を処理するためのより良い方法があるかもしれません。

変更ログ

  1. BlockingCollection に変更
  2. ConcurrentQueue に戻しました

私がチェックしたいくつかのこと:

4

2 に答える 2

3

ここでの最も簡単な解決策は、BlockingCollection(おそらくその を使用してGetConsumingEnumerable())長時間実行さTaskれる . 何もすることがないとき、これは a をThread無駄にしますが、1 つ無駄にするThreadことはそれほど悪いことではありません。

それを無駄にする余裕がない場合はThread、#3のようなものを使用できます. ただし、スレッドセーフにすることには細心の注意を払う必要があります。たとえば、コードでTaskが実行されておらずAddItem()、同時に 2 つのスレッドから呼び出された場合、最終的に 2 つTaskの が作成されますが、これはほぼ確実に間違っています。

.Net 4.5 を使用している場合の別のオプションは、ActionBlockTPL Dataflow から使用することです。これにより、スレッドを無駄にすることはなく、難しいスレッドセーフ コードを自分で記述する必要もありません。

于 2013-06-21T11:33:38.697 に答える