Solved

c# Queue calls to a method

Posted on 2015-01-07
3
183 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
3 Comments
 
LVL 33

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 33

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

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this article, I will show you HOW TO: Install VMware Tools for Windows on a VMware Windows virtual machine on a VMware vSphere Hypervisor 6.5 (ESXi 6.5) Host Server, using the VMware Host Client. The virtual machine has Windows Server 2016 instal…
The Windows functions GetTickCount and timeGetTime retrieve the number of milliseconds since the system was started. However, the value is stored in a DWORD, which means that it wraps around to zero every 49.7 days. This article shows how to solve t…
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
In this video, we discuss why the need for additional vertical screen space has become more important in recent years, namely, due to the transition in the marketplace of 4x3 computer screens to 16x9 and 16x10 screens (so-called widescreen format). …

756 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