7

I need single-producer, single-consumer FIFO query because

  • I need to process messages in the order they received.
  • I need to do this asynchronous because caller should not wait while I'm processing message.
  • Next message processing should be started only when previous message processing is finished. Sometimes frequency of "receiving" messages is higher than frequency of "processing" messages. But in average I should be able to process all messages, just sometimes I have to "queue" pack of them.

So it's pretty like TCP/IP I think, where you have one producer and one consumer, SOMETIMES you can receive messages faster than you can process, so you have to query them. Where order IS important and where caller absolutely not interested what you doing with that stuff.

This sounds easy enough and I likely can use general Queue for that, but I want to use BlockingCollection for that because I don't want to write any code with ManualResetEvent etc.

How suitable BlockingCollection for my task and probably you can suggest something else?

sll
  • 61,540
  • 22
  • 104
  • 156
Oleg Vazhnev
  • 23,239
  • 54
  • 171
  • 305

2 Answers2

12

BlockingCollection class implements IProducerConsumerCollection interface so perfectly fits your requirements.

You can create two Tasks, one for async producer and an other one as consumer worker. Former would add items to BlockingCollection and the latter just consume as soon as new are available in FIFO order.

Producer-consumer sample application using TPL Tasks and BlockingCollection:

class ProducerConsumer
{
    private static BlockingCollection<string> queue = new BlockingCollection<string>();

    static void Main(string[] args)
    {
        Start();
    }

    public static void Start()
    {
        var producerWorker = Task.Factory.StartNew(() => RunProducer());
        var consumerWorker = Task.Factory.StartNew(() => RunConsumer());

        Task.WaitAll(producerWorker, consumerWorker);
    }

    private static void RunProducer()
    {
        int itemsCount = 100;

        while (itemsCount-- > 0)
        {
            queue.Add(itemsCount + " - " + Guid.NewGuid().ToString());
            Thread.Sleep(250);
        }
    }

    private static void RunConsumer()
    {
        foreach (var item in queue.GetConsumingEnumerable())
        {
           Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.ffff") + " | " + item);
        }
    }
}

IProducerConsumerCollection:

Defines methods to manipulate thread-safe collections intended for producer/consumer usage. This interface provides a unified representation for producer/consumer collections so that higher level abstractions such as System.Collections.Concurrent.BlockingCollection(Of T) can use the collection as the underlying storage mechanism.

davidsbro
  • 2,761
  • 4
  • 23
  • 33
sll
  • 61,540
  • 22
  • 104
  • 156
  • I hope that default FIFO implementation of `BlockingCollection` will not be changed in some of the next releases, but this is another question... – Oleg Vazhnev Apr 11 '12 at 09:40
  • I believe you should be fine as long as you are referencing to `IProducerConsumerCollection` interface, and implementation of BCL classes can;t change in such principal way like FIFO order to something else – sll Apr 11 '12 at 10:14
  • 2
    I see the Start() method waits for the two tasks to complete. Obviously the `producerWorker` task will end, but the `consumerWorker` will never end. Should that be taken into account? – Rich Shealer Oct 07 '13 at 21:04
0

Since it's a queue you need, why not stick to a queue? You can use a Syncrhonized Queue .

zmbq
  • 38,013
  • 14
  • 101
  • 171
  • Martin right, [MSDN](http://msdn.microsoft.com/en-us/library/system.collections.queue.synchronized.aspx): `Enumerating through a collection is intrinsically not a thread-safe procedure. Even when a collection is synchronized, other threads can still modify the collection, which causes the enumerator to throw an exception. To guarantee thread safety during enumeration, you can either lock the collection during the entire enumeration or catch the exceptions resulting from changes made by other thread` – sll Apr 11 '12 at 10:34