Solved

c# Queue calls to a method

Posted on 2015-01-07
3
161 Views
Last Modified: 2015-01-15
Hi,

I have a method (SendandGet) that sends and receives data from a System.IO.SerialPort.

I have several different areas in my program that call this method at intervals and by the user.

I'm looking for a way to queue the calls to the method so that I can then handle them in synchronized way and make sure that every call to the method is executed. Each call to the Method will either return data or timeout but the code must finish before the next call is made.

I would like to do this on a separate thread. Not having any luck with TPL (Tasks or Task.Factory) but I might not be using it right.

Thank you.
0
Comment
Question by:SkyMagic
  • 2
3 Comments
 
LVL 32

Expert Comment

by:it_saige
ID: 40537836
Based on your stipulation (but the code must finish before the next call is made); TPL probably is not the way to go as it is designed to work with multiple threads in parallel.

You can easily make this work using a queue on a separate processing thread.  I did something similar to synchronize method calls between and client/server service.

Let me know if this might be what you are after.

-saige-
0
 

Author Comment

by:SkyMagic
ID: 40538345
hi saige,

Yes, a code example with a synchronized method call sounds good.

Looking forward to trying this solution.
Thank you
0
 
LVL 32

Accepted Solution

by:
it_saige earned 500 total points
ID: 40540419
Here is a small example:
using System;
using System.Collections.Generic;
using System.Threading;

namespace EE_Q28592773
{
	class Program
	{
		#region CommandQueued Members
		[NonSerialized]
		/// <summary>Raised when a <see cref="Command"/> is queued.</summary>
		private CommandQueuedEventHandler _commandQueued;
		/// <summary>Raised when a <see cref="Command"/> is queued.</summary>
		public event CommandQueuedEventHandler CommandQueued
		{
			add { _commandQueued += value; }
			remove { _commandQueued -= value; }
		}

		/// <summary>Invoked whenever a <see cref="Command"/> is queued.  The <see cref="Command"/> queued is contained in the event data.</summary>
		/// <param name="sender">The source of the event data.</param>
		/// <param name="e">The event data that contains the <see cref="Command"/> that was queued.</param>
		protected virtual void OnCommandQueued(object sender, CommandQueuedEventArgs e)
		{
			CommandQueuedEventHandler handler = _commandQueued;
			if (handler != null)
				handler(sender, e);
		}
		#endregion

		#region CommandReceived Members
		[NonSerialized]
		/// <summary>Raised when a <see cref="Command"/> is received.</summary>
		private CommandReceivedEventHandler _commandReceived;
		/// <summary>Raised when a <see cref="Command"/> is received.</summary>
		public event CommandReceivedEventHandler CommandReceived
		{
			add { _commandReceived += value; }
			remove { _commandReceived -= value; }
		}

		/// <summary>Invoked whenever a <see cref="Command"/> is received.  The <see cref="Command"/> received is contained in the event data.</summary>
		/// <param name="sender">The source of the event data.</param>
		/// <param name="e">The event data that contains the <see cref="Command"/> that was received.</param>
		protected virtual void OnCommandReceived(object sender, CommandReceivedEventArgs e)
		{
			CommandReceivedEventHandler handler = _commandReceived;
			if (handler != null)
				handler(sender, e);
		}
		#endregion

		/// <summary>This is used to determine if the queue is accepting messages.</summary>
		/// <remarks>We could use this to tell the queue to stop accepting messages but allow us to still process the messages in queue or enroute.</remarks>
		private bool IsQueueAcceptingMessages = false;
		/// <summary>This is used to determine if the queue is processing messages.</summary>
		/// <remarks>We use this to prevent the OnQueuedEvent from firing multiple times.</remarks>
		private bool IsQueueProcessingMessages = false;
		/// <summary>This is used to determine if the queue is active.</summary>
		/// <remarks>We use this to hold keep the queue processing thread alive.</remarks>
		private bool IsQueueActive = false;
		/// <summary>The instance</summary>
		private static Program Instance;

		/// <summary>The message queue</summary>
		private readonly Queue<Command> MessageQueue = new Queue<Command>();
		/// <summary>The message arrived auto reset event.</summary>
		private readonly AutoResetEvent MessageArrived = new AutoResetEvent(true);

		/// <summary>Defines the entry point of the application.</summary>
		/// <param name="args">The arguments.</param>
		static void Main(string[] args)
		{
			Instance = new Program();
			Instance.Start();
			Instance.Stop();
			Console.WriteLine("Press any key to close this window");
			Console.ReadLine();
		}

		/// <summary>Starts this instance.</summary>
		private void Start()
		{
			IsQueueActive = true;
			IsQueueAcceptingMessages = true;
			IsQueueProcessingMessages = true;
			CommandReceived += OnReceivedCommand;
			CommandQueued += OnQueuedCommand;
			ThreadPool.QueueUserWorkItem(OnQueuedMessage);
			for (int i = 0; i < 10; i++)
				OnReceivedCommand(this, new CommandReceivedEventArgs(new Command(string.Format("This is a batch 1 of test messages.  Message {0}", i), true)));
			Console.WriteLine("Sent batch 1 to the queue");
			Console.WriteLine();
			OnReceivedCommand(this, new CommandReceivedEventArgs(new Command("This is a batch 2 of test messages.", 10)));
			Console.WriteLine("Sent batch 2 to the queue");
			Console.WriteLine();
			OnReceivedCommand(this, new CommandReceivedEventArgs(new Command("This is a batch 3 of test messages.", 5)));
			Console.WriteLine("Sent batch 3 to the queue");
			Console.WriteLine();
			Console.ReadLine();
		}

		/// <summary>Stops this instance.</summary>
		private void Stop()
		{
			IsQueueAcceptingMessages = false;
			IsQueueActive = false;
		}

		/// <summary>Consoles the writer.</summary>
		/// <param name="message">The message.</param>
		/// <param name="repeatNumberOfTimes">The repeat number of times.</param>
		/// <param name="addLineBreak">if set to <c>true</c> [add line break].</param>
		private static void ConsoleWriter(string message, int repeatNumberOfTimes, bool addLineBreak)
		{
			for (int i = 0; i <= repeatNumberOfTimes; i++)
				Console.WriteLine(message);

			if (addLineBreak)
				Console.WriteLine();
		}

		/// <summary>Event that fire when messages have been added to the <see cref="Command"/> queue.</summary>
		private void OnQueuedMessage(object state)
		{
			while (IsQueueActive)
			{
				if (IsQueueAcceptingMessages)
				{
					while (MessageQueue.Count > 0)
					{
						try
						{
							Command value = MessageQueue.Dequeue();
							ConsoleWriter(value.Message, value.RepeatNumberOfTimes, value.AddLineBreak);
						}
						catch (Exception ex)
						{
							Console.WriteLine(string.Format("Exception reported in {0} - {1} [{2}]", ex.Source, ex, ex.Message));
							Console.WriteLine();
						}
					}
				}
				else
				{
					while (MessageQueue.Count > 0)
					{
						try
						{
							Command value = MessageQueue.Dequeue();
							ConsoleWriter(value.Message, value.RepeatNumberOfTimes, value.AddLineBreak);
						}
						catch (Exception ex)
						{
							Console.WriteLine(string.Format("Exception reported in {0} - {1} [{2}]", ex.Source, ex, ex.Message));
							Console.WriteLine();
						}
					}
				}
				IsQueueProcessingMessages = false;
				MessageArrived.WaitOne();
			}
		}

		/// <summary>Handles the <see cref="E:ReceivedCommand" /> event.</summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="CommandReceivedEventArgs"/> instance containing the event data.</param>
		private void OnReceivedCommand(object sender, CommandReceivedEventArgs e)
		{
			try
			{
				MessageQueue.Enqueue(e.Command);
				OnCommandQueued(this, new CommandQueuedEventArgs(e.Command));
			}
			catch (Exception ex)
			{
				Console.WriteLine(string.Format("Exception reported in {0} - {1} [{2}]", ex.Source, ex, ex.Message));
				Console.WriteLine();
			}
		}

		/// <summary>Handles the <see cref="E:QueuedCommand" /> event.</summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="CommandQueuedEventArgs"/> instance containing the event data.</param>
		private void OnQueuedCommand(object sender, CommandQueuedEventArgs e)
		{
			try
			{
				if (!IsQueueProcessingMessages)
				{
					IsQueueProcessingMessages = true;
					MessageArrived.Set();
				}
			}
			catch (Exception ex)
			{
				Console.WriteLine(string.Format("Exception reported in {0} - {1} [{2}]", ex.Source, ex, ex.Message));
				Console.WriteLine();
			}
		}
	}

	/// <summary>Serializable class that handles the command structure.</summary>
	[Serializable]
	public sealed class Command
	{
		#region Properties
		/// <summary>Gets the message.</summary>
		/// <value>The message.</value>
		public string Message { get; private set; }
		/// <summary>Gets the number of times to repeat the message.</summary>
		/// <value>The number of times to repeat the message.</value>
		public int RepeatNumberOfTimes { get; private set; }
		/// <summary>Gets a value indicating whether to add line break.</summary>
		/// <value>If set to <c>true</c> adds a line break when messages are printed.</value>
		public bool AddLineBreak { get; private set; }
		#endregion

		#region Public Methods
		/// <summary>Prevents a default instance of the <see cref="Command"/> class from being created.</summary>
		private Command() { ;}

		/// <summary>Initializes a new instance of the <see cref="Command"/> class.</summary>
		/// <param name="Message">The message.</param>
		public Command(string Message) : this(Message, 0, true) { ;}

		/// <summary>Initializes a new instance of the <see cref="Command"/> class.</summary>
		/// <param name="Message">The message.</param>
		/// <param name="RepeatNumberOfTimes">The number of times to repeat the message.</param>
		public Command(string Message, int RepeatNumberOfTimes) : this(Message, RepeatNumberOfTimes, true) { ;}

		/// <summary>Initializes a new instance of the <see cref="Command"/> class.</summary>
		/// <param name="Message">The message.</param>
		/// <param name="AddLineBreak">If set to <c>true</c> adds a line break when messages are printed.</param>
		public Command(string Message, bool AddLineBreak) : this(Message, 0, AddLineBreak) { ;}

		/// <summary>Initializes a new instance of the <see cref="Command"/> class.</summary>
		/// <param name="Message">The message.</param>
		/// <param name="RepeatNumberOfTimes">The number of times to repeat the message.</param>
		/// <param name="AddLineBreak">If set to <c>true</c> adds a line break when messages are printed.</param>
		public Command(string Message, int RepeatNumberOfTimes, bool AddLineBreak)
		{
			// The constructor will only run when a new instance is created, i.e. at
			// Broadcast time, not at de-serialisation time, hence the sending computername
			// will be the machine name where the item was created
			this.Message = Message;
			this.RepeatNumberOfTimes = RepeatNumberOfTimes;
			this.AddLineBreak = AddLineBreak;
		}
		#endregion
	}

	/// <summary>Represents the method that will handle the <see cref="CommandQueued"/> event.</summary>
	/// <param name="sender">The source of the event.</param>
	/// <param name="message">A <see cref="CommandQueuedEventArgs"/> object that contains the event data.</param>
	public delegate void CommandQueuedEventHandler(object sender, CommandQueuedEventArgs e);

	public class CommandQueuedEventArgs : EventArgs
	{
		/// <summary>Gets the command.</summary>
		/// <value>The command.</value>
		public Command Command { get; private set; }

		/// <summary>Initializes a new instance of the <see cref="CommandQueuedEventArgs"/>.</summary>
		private CommandQueuedEventArgs()
		{
			Command = default(Command);
		}

		/// <summary>Initializes a new instance of the <see cref="CommandQueuedEventArgs"/>.</summary>
		/// <param name="Command">The <see cref="Command"/> object that will be queued.</param>
		public CommandQueuedEventArgs(Command Command)
		{
			this.Command = Command;
		}
	}

	/// <summary>Represents the method that will handle the <see cref="CommandReceived"/> event.</summary>
	/// <param name="sender">The source of the event.</param>
	/// <param name="message">A <see cref="CommandReceivedEventArgs"/> object that contains the event data.</param>
	public delegate void CommandReceivedEventHandler(object sender, CommandReceivedEventArgs e);

	public class CommandReceivedEventArgs : EventArgs
	{
		/// <summary>Gets the command.</summary>
		/// <value>The command.</value>
		public Command Command { get; private set; }

		/// <summary>Initializes a new instance of the <see cref="CommandReceivedEventArgs"/>.</summary>
		private CommandReceivedEventArgs()
		{
			Command = default(Command);
		}

		/// <summary>Initializes a new instance of the <see cref="CommandReceivedEventArgs"/>.</summary>
		/// <param name="ICommandShell">The <see cref="Command"/> object that was recieved.</param>
		public CommandReceivedEventArgs(Command Command)
		{
			this.Command = Command;
		}
	}
}

Open in new window

Produces the following output -Capture.JPGYou can verify that this is a multi-threaded application because the Console.Writes for 'Sent batch x to queue' are sometimes printed before a batch has processed (even though their calls happen after you send the batch to the queue via the event).

-saige-
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
When you start your Windows 10 PC and got an "Operating system not found" error or just saw  "Auto repair for startup". After a while, you have entered a loop for Auto repair which does not fix anything and you will be in a  panic as all your work w…
With the advent of Windows 10, Microsoft is pushing a Get Windows 10 icon into the notification area (system tray) of qualifying computers. There are many reasons for wanting to remove this icon. This two-part Experts Exchange video Micro Tutorial s…
With the advent of Windows 10, Microsoft is pushing a Get Windows 10 icon into the notification area (system tray) of qualifying computers. There are many reasons for wanting to remove this icon. This two-part Experts Exchange video Micro Tutorial s…

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now