web-dev-qa-db-ja.com

ブロッキングコレクションとタスクを使用した古典的な生産者/消費者パターン.net4 TPL

以下の擬似コードを参照してください

//Single or multiple Producers produce using below method
    void Produce(object itemToQueue)
    {
        concurrentQueue.enqueue(itemToQueue);
        consumerSignal.set;
    }

    //somewhere else we have started a consumer like this
    //we have only one consumer
    void StartConsumer()
    {
        while (!concurrentQueue.IsEmpty())
        {
            if (concurrentQueue.TrydeQueue(out item))
            {
                //long running processing of item
            }
        }
        consumerSignal.WaitOne();
    }

太古の昔から使用していたこのパターンを移植して、タスクファクトリーで作成されたタスクとネット4の新しいシグナリング機能を使用するにはどうすればよいですか。つまり、誰かがネット4を使用してこのパターンを作成した場合、どのようになりますか。擬似コードは問題ありません。ご覧のとおり、Iamはすでに.net 4concurrentQueueを使用しています。タスクを使用し、可能であれば新しいシグナリングメカニズムを使用するにはどうすればよいですか。ありがとう

Jon/Danのおかげで以下の私の問題の解決策。甘い。手動シグナリングや、昔のようなwhile(true)またはwhile(itemstoProcess)タイプのループはありません

//Single or multiple Producers produce using below method
 void Produce(object itemToQueue)
 {
     blockingCollection.add(item);
 }

 //somewhere else we have started a consumer like this
 //this supports multiple consumers !
 task(StartConsuming()).Start; 

 void StartConsuming()
 {
     foreach (object item in blockingCollection.GetConsumingEnumerable())
     {
                //long running processing of item
     }
 }

cancellations are handled using cancel tokens
20
Gullu

BlockingCollection<T> 。ドキュメントに例があります。

そのクラスは、これを簡単にするために特別に設計されています。

23
Jon Skeet

コードの2番目のブロックの方が見栄えがします。しかし、Taskを開始し、すぐにそれを待つことは無意味です。 Takeを呼び出してから、消費スレッドで直接返されたアイテムを処理するだけです。これが、生産者/消費者パターンの実行方法です。作業項目の処理がより多くの消費者を保証するのに十分集中的であると思う場合は、必ずより多くの消費者を開始してください。 BlockingCollectionは安全な複数のプロデューサーおよび複数のコンシューマーです。

public class YourCode
{
  private BlockingCollection<object> queue = new BlockingCollection<object>();

  public YourCode()
  {
    var thread = new Thread(StartConsuming);
    thread.IsBackground = true;
    thread.Start();
  }

  public void Produce(object item)
  {
    queue.Add(item);
  }

  private void StartConsuming()
  {
    while (true)
    {
      object item = queue.Take();
      // Add your code to process the item here.
      // Do not start another task or thread. 
    }
  }
}
11
Brian Gideon

以前、ある種の「オンデマンド」キューコンシューマー(ConcurrentQueueからのコンシューマーに基づく)を作成するパターンを使用しました。

        private void FireAndForget(Action fire)
        {
            _firedEvents.Enqueue(fire);
            lock (_taskLock)
            {
                if (_launcherTask == null)
                {
                    _launcherTask = new Task(LaunchEvents);
                    _launcherTask.ContinueWith(EventsComplete);
                    _launcherTask.Start();
                }
            }
        }

        private void LaunchEvents()
        {
            Action nextEvent;

            while (_firedEvents.TryDequeue(out nextEvent))
            {
                if (_synchronized)
                {
                    var syncEvent = nextEvent;
                    _mediator._syncContext.Send(state => syncEvent(), null);
                }
                else
                {
                    nextEvent();                        
                }

                lock (_taskLock)
                {
                    if (_firedEvents.Count == 0)
                    {
                        _launcherTask = null;
                        break;
                    }
                }
            }
        }

        private void EventsComplete(Task task)
        {
            if (task.IsFaulted && task.Exception != null)
            {
                 // Do something with task Exception here
            }
        }
1
Dan Bryant