C# I need a FIFO list\thread in order to provide a buffer for printing

I need to Add items to a list (for example) through one thread, and access\remove them from the list FIFO from another.  The second process, must kick in every time the list has items added or keep running as a background thread.

A simple example would be highly appreciated.
dbdpAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

it_saigeDeveloperCommented:
You could use a Queue (for .NET 3.5 - you will need to implement a lock object for thread saftey) or a ConcurrentQueue (for .NET 4 and above - thread safe queue)

.NET 3.5 Example using a standard queue:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace EE_Q28768639
{
	class Program
	{
		static readonly Messages messages = new Messages();
		static AutoResetEvent reset = new AutoResetEvent(true);
		static bool isProcessing = true;

		static void Main(string[] args)
		{
			messages.Arrived += OnArrived;
			ThreadPool.QueueUserWorkItem(ProcessMessages);
			ThreadPool.QueueUserWorkItem(CreateMessages);
			Console.WriteLine("Finished processing messages");
			Console.ReadLine();
			messages.Arrived -= OnArrived;
		}

		private static void OnArrived(object sender, EventArgs e)
		{
			reset.Set();
		}

		private static void ProcessMessages(object state)
		{
			while (isProcessing)
			{
				while (messages.Count > 0)
					Console.WriteLine("Dequeued - {0}", messages.Dequeue());
				reset.WaitOne();
			}

			while (messages.Count > 0)
				Console.WriteLine("Dequeued - {0}", messages.Dequeue());
		}

		private static void CreateMessages(object state)
		{
			for (int i = 0; i < 10; i++)
			{
				Console.WriteLine("Creating message for {0}", i);
				messages.Enqueue(string.Format("Message{0}", i));
				Thread.Sleep(500);
			}
			isProcessing = false;
			reset.Set();
		}
	}

	class Messages : Queue<string>
	{
		[NonSerialized]
		private EventHandler<EventArgs> _arrived;
		public event EventHandler<EventArgs> Arrived
		{
			add { _arrived += value; }
			remove { _arrived -= value; }
		}

		protected virtual void OnArrived(object sender, EventArgs e)
		{
			EventHandler<EventArgs> handler = _arrived;
			if (handler != null)
				handler(sender, e);
		}

		private readonly object _locker = new object();

		public Messages() : base() { ;}

		public Messages(int capacity) : base(capacity) { ;}

		public Messages(IEnumerable<string> source) : base(source) { ;}

		public void Enqueue(string item)
		{
			lock (_locker)
			{
				base.Enqueue(item);
				OnArrived(this, EventArgs.Empty);
			}
		}

		public string Dequeue()
		{
			lock (_locker)
			{
				return base.Dequeue();
			}
		}
	}
}

Open in new window

Which produces the following output -Capture.JPG
it_saigeDeveloperCommented:
And a .NET 4 example using a ConcurrentQueue:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace EE_Q28768639
{
	class Program
	{
		static readonly Messages messages = new Messages();
		static AutoResetEvent reset = new AutoResetEvent(true);

		static void Main(string[] args)
		{
			CancellationTokenSource source = new CancellationTokenSource();
			CancellationToken token = source.Token;
			messages.Arrived += OnArrived;

			Task.Factory.StartNew(() =>
				{
					Console.WriteLine("Producer has started...");
					for (int i = 0; i < 10; i++)
					{
						Console.WriteLine("Creating message for {0}", i);
						messages.Enqueue(string.Format("Message{0}", i));
						Thread.Sleep(500);
					}
					source.Cancel();
					reset.Set();
				});

			Task.Factory.StartNew(() =>
				{
					string result = string.Empty;
					Console.WriteLine("Consumer has started...");
					while (!token.IsCancellationRequested)
					{
						while (messages.Count > 0)
						{
							if (messages.TryDequeue(out result))
								Console.WriteLine("Dequeued - {0}", result);
							reset.WaitOne();
						}
					}
				}, token).ContinueWith(t => 
				{
					string result = string.Empty;
					while (messages.Count > 0)
					{
						if (messages.TryDequeue(out result))
							Console.WriteLine("Dequeued - {0}", result);
					}
					Console.WriteLine("Finished dequeing all messages.");
					Console.WriteLine("Press any key to exit...");
				});
			Console.ReadKey();
			messages.Arrived -= OnArrived;
		}

		private static void OnArrived(object sender, EventArgs e)
		{
			reset.Set();
		}
	}

	class Messages : ConcurrentQueue<string>
	{
		[NonSerialized]
		private EventHandler<EventArgs> _arrived;
		public event EventHandler<EventArgs> Arrived
		{
			add { _arrived += value; }
			remove { _arrived += value; }
		}

		protected virtual void OnArrived(object sender, EventArgs e)
		{
			EventHandler<EventArgs> handler = _arrived;
			if (handler != null)
				handler(sender, e);
		}

		public Messages() : base() { ;}

		public Messages(IEnumerable<string> source) : base(source) { ;}

		public void Enqueue(string item)
		{
			base.Enqueue(item);
			OnArrived(this, EventArgs.Empty);
		}
	}
}

Open in new window

Which produces the following output -Capture.JPG-saige-
dbdpAuthor Commented:
Hi, thank you for that.

However, if I may ask, let's say the queue was completely dequeued and then I added further items, would the dequeue kick in again automatically?
it_saigeDeveloperCommented:
Yes it would, the reason is because the Arrived event fires causing a reset of the autoreset event.

In the .NET 3.5 example the ProcessQueue method depends on the isProcessing boolean value to still be true; while in the .NET 4 example a cancellation token is used.

In both examples, however, you will notice that after the queue is exhausted that the AutoResetEvent is waited (this pauses the thread waiting for the set method to be called on the AutoResetEvent).

In either case, in the .NET 3.5 example, the isProcessing boolean is set to false to indicate that the processing would stop and the set method is called on the reset event; this is different in the .NET 4 example where the Thread handling the message creation calls the cancellation token.

Just one modification needs to be made though (a pesky bug has been located in the .NET 4.0 Example):
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace EE_Q28768639
{
	class Program
	{
		static readonly Messages messages = new Messages();
		static AutoResetEvent reset = new AutoResetEvent(true);

		static void Main(string[] args)
		{
			CancellationTokenSource source = new CancellationTokenSource();
			CancellationToken token = source.Token;
			messages.Arrived += OnArrived;

			Task.Factory.StartNew(() =>
				{
					Console.WriteLine("Producer has started...");
					for (int i = 0; i < 10; i++)
					{
						Console.WriteLine("Creating message for {0}", i);
						messages.Enqueue(string.Format("Message{0}", i));
						Thread.Sleep(500);
					}
					source.Cancel();
					reset.Set();
				});

			Task.Factory.StartNew(() =>
				{
					string result = string.Empty;
					Console.WriteLine("Consumer has started...");
					while (!token.IsCancellationRequested)
					{
						while (messages.Count > 0)
						{
							if (messages.TryDequeue(out result))
								Console.WriteLine("Dequeued - {0}", result);
						}
						// This needs to be outside of the messages.Count loop. 
						//We want to set it after we read the messages, not after reading the first one.
						reset.WaitOne();
					}
				}, token).ContinueWith(t => 
				{
					string result = string.Empty;
					while (messages.Count > 0)
					{
						if (messages.TryDequeue(out result))
							Console.WriteLine("Dequeued - {0}", result);
					}
					Console.WriteLine("Finished dequeing all messages.");
					Console.WriteLine("Press any key to exit...");
				});
			Console.ReadKey();
			messages.Arrived -= OnArrived;
		}

		private static void OnArrived(object sender, EventArgs e)
		{
			reset.Set();
		}
	}

	class Messages : ConcurrentQueue<string>
	{
		[NonSerialized]
		private EventHandler<EventArgs> _arrived;
		public event EventHandler<EventArgs> Arrived
		{
			add { _arrived += value; }
			remove { _arrived += value; }
		}

		protected virtual void OnArrived(object sender, EventArgs e)
		{
			EventHandler<EventArgs> handler = _arrived;
			if (handler != null)
				handler(sender, e);
		}

		public Messages() : base() { ;}

		public Messages(IEnumerable<string> source) : base(source) { ;}

		public void Enqueue(string item)
		{
			base.Enqueue(item);
			OnArrived(this, EventArgs.Empty);
		}
	}
}

Open in new window

-saige-

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
dbdpAuthor Commented:
Thank you very much
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Enterprise Software

From novice to tech pro — start learning today.