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.
- First of all, the shared queue is instantiated.
- Then I set a timer to simulate the arrival of a keep alive message (first producer).
- Then I created the consumer thread (
processing
object). - Then I created another producer thread (
generating
object). 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?