5

I have a C# 4.0 app with "high priority" and "low priority" queues implemented as such:

BlockingCollection highPriority = new BlockingCollection(1000); BlockingCollection lowPriority = new BlockingCollection(1000);

Any data produced in highPriority should be consumed before any data produced in lowPriority. The twist here is that data may be produced to either of the two queues at any time. So after I've consumed all of the data in highPriority, I will then being consuming any data that might be in lowPriority. If new data is produced in highPriority while I'm consuming data in lowPriority, I want to finish consuming the current item in lowPriority and then switch back and process the data in highPriority.

Can anyone suggest an algorithm to help with this? Pseudo code is fine. Thanks very much.

bmt22033
  • 6,880
  • 14
  • 69
  • 98

3 Answers3

3

How about this:

while(true)
{
    workitem = highQueue.dequeue();

    if(workitem == null)
        workitem = lowQueueu.dequeue()

    process(workitem)
}
Carra
  • 17,808
  • 7
  • 62
  • 75
  • I like to abstract this away if you need to provide this to other client classes - just make your own `IProducerConsumerCollection` class that contains both of these (high and low priority) and then provide back to the client classes an instance of `BlockingCollection` backed by that special `IProducerConsunmerCollection`. Your custom class then can provide the available object which Carra's solution internally. – Kevin Brock Apr 01 '11 at 17:26
  • An interesting idea, but your code misses some fundamental problems, like what happens when both queues are empty. Also, your loop is basically a busy wait that continually checks for items rather than using a wait handle of some sort. I wouldn't recommend this solution. – Jim Mischel Apr 01 '11 at 18:03
  • It's simplified. I'd normally add a if(workitem != null) process(workitem) else Thread.Sleep(100) or something similar. – Carra Apr 01 '11 at 18:10
1

You'll want to wrap this into a single object if you can, as @Kevin Brock suggested, and have that object implement IProducerConsumerCollection. Otherwise your code that calls TryDequeue will have do do a busy wait loop. That is, with two queues, you have to write something like:

WorkItem item = null;
do
{
    if (!hpQueue.TryDequeue(out item))
    {
        lpQueue.TryDequeue(out item);
    }
while (item != null);

If you use your own class, then you can use events (EventWaitHandle, etc.) to prevent the busy waiting.

In truth, you'd probably be better off using a priority queue. It would be pretty easy to make a priority queue thread-safe and implement IProducerConsumerCollection, and then you can use it with BlockingCollection. A good place to start would be Julian Bucknall's Priority Queue in C#.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
1

I would do this with a single priority queue. This would allow you to add a 3rd priority later, with very little code change.

I've written one before using a lockFree-SkipList, But here's a project for one which uses a normal skip-list (http://www.codeproject.com/KB/recipes/PriorityQueueSkipList.aspx). I used a skip-list because they preform well under concurrency and are pretty simple to implement (minus the lock-free version).

Ive also seen a priority queue on CodePlex which use a Red-Black Tree, but I can't find it right now. UPDATE : The priority queue implementation I was thinking of was part of the NGenerics project: http://code.google.com/p/ngenerics/

eSniff
  • 5,713
  • 1
  • 27
  • 31