0

I'm creating a console application that should receive messages from the network in order to process them. First I created a singleton class to ensure that the same queue is accessible by all classes: this class is ProcessingQueue.

public class ProcessingQueue
{
    public class ItemToProcess
    {
        public string SourceClientId { get; set; }
        public IMessage ReceivedMessage { get; set; }
    }

    private int m_MaxSize = 20;
    private Queue<ItemToProcess> m_InternalQueue;

    private static volatile ProcessingQueue m_Instance = null;
    private static readonly object syncRoot = new object();

    private ProcessingQueue()
    {
        m_InternalQueue = new Queue<ItemToProcess>();
    }

    public static ProcessingQueue Instance
    {
        get
        {
            if (m_Instance == null)
            {
                lock (syncRoot)
                {
                    if (m_Instance == null)
                    {
                        m_Instance = new ProcessingQueue();
                    }
                }
            }
            return m_Instance;
        }
    }

    public int MaxSize
    {
        get
        {
            lock (syncRoot)
            {
                return m_MaxSize;
            }
        }
        set
        {
            if (value > 0)
            {
                lock (syncRoot)
                {
                    m_MaxSize = value;
                }
            }
        }
    }

    public void Enqueue(String source, IMessage message)
    {
        lock (syncRoot)
        {
            while (m_InternalQueue.Count >= m_MaxSize)
            {
                Monitor.Wait(syncRoot);
            }
            m_InternalQueue.Enqueue(new ItemToProcess { SourceClientId = source, ReceivedMessage = message });
            if (m_InternalQueue.Count == 1)
            {
                Monitor.PulseAll(syncRoot);
            }
        }
    }

    public ItemToProcess Dequeue()
    {
        lock (syncRoot)
        {
            while (m_InternalQueue.Count == 0)
            {
                Monitor.Wait(syncRoot);
            }
            ItemToProcess item = m_InternalQueue.Dequeue();
            if (m_InternalQueue.Count == m_MaxSize - 1)
            {
                Monitor.PulseAll(syncRoot);
            }
            return item;
        }
    }

    public int Count
    {
        get
        {
            lock (syncRoot)
            {
                return m_InternalQueue.Count;
            }
        }
    }
}

Then I implemented the main class of the project as follows.

  1. First of all, the shared queue is instantiated.
  2. Then I set a timer to simulate the arrival of a keep alive message (first producer).
  3. Then I created the consumer thread (processing object).
  4. Then I created another producer thread (generating object).
  5. Finally, I run all the threads and the timer.

    class Program { static ProcessingQueue queue = ProcessingQueue.Instance; static System.Timers.Timer keep_alive_timer = new System.Timers.Timer(10000);

    private static volatile bool running = true;
    
    
    static void Main(string[] args)
    {
        queue.MaxSize = 30;
        keep_alive_timer.Elapsed += new ElapsedEventHandler(delegate(object sender, ElapsedEventArgs e)
        {
            KeepAliveMessage msg = new KeepAliveMessage(Guid.NewGuid());
            Console.WriteLine("Keep Alive: " + msg.MsgId);
            queue.Enqueue("", msg);
        });
        keep_alive_timer.Enabled = true;
        keep_alive_timer.AutoReset = true;
    
        Thread processing = new Thread(delegate()
        {
            while (running)
            {
                Console.WriteLine("Number of elements in queue: {0}", queue.Count);
    
                ProcessingQueue.ItemToProcess msg = queue.Dequeue();
                Console.WriteLine("Processed: msgid={0}, source={1};", msg.ReceivedMessage.MsgId, msg.SourceClientId);
    
                Thread.Sleep(1500);
            }
        });
    
        Thread generating = new Thread(MessagesFromNetwork);
    
        processing.Start();
        keep_alive_timer.Start();
        generating.Start();
    
        Console.WriteLine("RUNNING...\n");
        Console.ReadLine();
    
        running = false;
        keep_alive_timer.Stop();
        Console.WriteLine("CLOSING...\n");
    
        //processing.Abort();
        //generating.Abort();
    
        bool b1 = processing.Join(TimeSpan.FromSeconds(5));
        bool b2 = generating.Join(TimeSpan.FromSeconds(5));
    
        Console.WriteLine("b1 {0}", b1);
        Console.WriteLine("b2 {0}", b2);
        Console.WriteLine("END");
        Console.ReadLine();
    }
    
    static void MessagesFromNetwork()
    {
        string[] sourceClients = { "1", "2", "3", "4", "5" };
        while (running)
        {
            IMessage msg; // interface IMessage
            Random random = new Random();
            int type = random.Next(2);
            switch (type)
            {
                case 0:
                    msg = new KeepAliveMessage(Guid.NewGuid());   // implements IMessage
                    break;
                case 1:
                    msg = new TaskMessage(Guid.NewGuid(), ...);   // implements IMessage
                    break;
                default:
                    throw new Exception("Messaggio non valido!");
            }
            Console.WriteLine("New message received: " + msg.MsgId);
            queue.Enqueue(sourceClients[random.Next(sourceClients.Length)], msg);
            Console.WriteLine("... message enqueued: " + msg.MsgId);
            Thread.Sleep(500);
        }
    }
    

    }

Pressing Enter during the execution, the running variable becomes false and both the thread should terminate. However this does not always happen, in fact one of the two methods Join did not return the control: for this reason I specified a timeout within the Join methods, but after Console.WriteLine("END"); the console application freezes (the second Join returns false).

Maybe the second thread is not terminated properly ... Why?

enzom83
  • 7,500
  • 8
  • 55
  • 107
  • Singleton is much easier to achieve with `Lazy`, but I always thought Singleton is the crudiest of patterns. http://stackoverflow.com/a/1020384/14357 – spender Jul 01 '12 at 22:47
  • Ok, but I need a queue accessible to instances of a WCF service: in my code I simulate data from the network, but in reality many instances of a WCF service must put data in the queue. – enzom83 Jul 01 '12 at 22:58

1 Answers1

1

Seems like Dequeue or Enqueue could go into a Monitor.Wait() , when running is stopped nobody is pulsed.

You wait 5 seconds but note that MaxSize * 1500 > 5000

I couldn't so directly find out the Timer frequency.

Henk Holterman
  • 236,989
  • 28
  • 287
  • 464