Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Threading and Queues

This isn't about the different methods I could or should be using to utilize the queues in the best manner, rather something I have seen happening that makes no sense to me.

void Runner() {
    // member variable
    queue = Queue.Synchronized(new Queue());
    while (true) {
        if (0 < queue.Count) {
            queue.Dequeue();
        }
    }
}

This is run in a single thread:

var t = new Thread(Runner);
t.IsBackground = true;
t.Start();

Other events are "Enqueue"ing else where. What I've seen happen is over a period of time, the Dequeue will actually throw InvalidOperationException, queue empty. This should be impossible seeing as how the count guarantees there is something there, and I'm positive that nothing else is "Dequeue"ing.

The question(s):

  1. Is it possible that the Enqueue actually increases the count before the item is fully on the queue (whatever that means...)?
  2. Is it possible that the thread is somehow restarting (expiring, reseting...) at the Dequeue statement, but immediately after it already removed an item?

Edit (clarification):

These code pieces are part of a Wrapper class that implements the background helper thread. The Dequeue here is the only Dequeue, and all Enqueue/Dequeue are on the Synchronized member variable (queue).

like image 219
neouser99 Avatar asked Apr 27 '09 16:04

neouser99


2 Answers

Using Reflector, you can see that no, the count does not get increased until after the item is added.

As Ben points out, it does seem as you do have multiple people calling dequeue.

You say you are positive that nothing else is calling dequeue. Is that because you only have the one thread calling dequeue? Is dequeue called anywhere else at all?

EDIT:

I wrote a little sample code, but could not get the problem to reproduce. It just kept running and running without any exceptions.

How long was it running before you got errors? Maybe you can share a bit more of the code.

class Program
{
    static Queue q = Queue.Synchronized(new Queue());
    static bool running = true;

    static void Main()
    {
        Thread producer1 = new Thread(() =>
            {
                while (running)
                {
                    q.Enqueue(Guid.NewGuid());
                    Thread.Sleep(100);
                }
            });

        Thread producer2 = new Thread(() =>
        {
            while (running)
            {
                q.Enqueue(Guid.NewGuid());
                Thread.Sleep(25);
            }
        });

        Thread consumer = new Thread(() =>
            {
                while (running)
                {
                    if (q.Count > 0)
                    {
                        Guid g = (Guid)q.Dequeue();
                        Console.Write(g.ToString() + " ");
                    }
                    else
                    {
                        Console.Write(" . ");
                    }
                    Thread.Sleep(1);
                }
            });
        consumer.IsBackground = true;

        consumer.Start();
        producer1.Start();
        producer2.Start();

        Console.ReadLine();

        running = false;
    }
}
like image 64
Erich Mirabal Avatar answered Sep 28 '22 07:09

Erich Mirabal


Here is what I think the problematic sequence is:

  1. (0 < queue.Count) evaluates to true, the queue is not empty.
  2. This thread gets preempted and another thread runs.
  3. The other thread removes an item from the queue, emptying it.
  4. This thread resumes execution, but is now within the if block, and attempts to dequeue an empty list.

However, you say nothing else is dequeuing...

Try outputting the count inside the if block. If you see the count jump numbers downwards, someone else is dequeuing.

like image 22
Ben S Avatar answered Sep 28 '22 06:09

Ben S