Link to home
Start Free TrialLog in
Avatar of dbdp
dbdp

asked on

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.
Avatar of it_saige
it_saige
Flag of United States of America image

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 -User generated image
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 -User generated image-saige-
Avatar of dbdp
dbdp

ASKER

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?
ASKER CERTIFIED SOLUTION
Avatar of it_saige
it_saige
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of dbdp

ASKER

Thank you very much