Blog

Multithreading and Delegates in C# and Handling a Parameter Mismatch Exception

Multithreading and Delegates in C# and Handling a Parameter Mismatch Exception

Recently I was working on a project using several threads in C#. I had four threads; one for the Main UI, one for serial communication with some force sensors, one operating a motion controller, and the final one used to perform user defined scripts. When using multiple threads it is important to control the method of information transfer between threads. Although some older software required you to create your own flags or methods to prevent multiple access to the same point, C# allows you access to several ways to do this that are both easy to use and very robust.

There are several different ways you can correctly use delegates, however I have found the easiest and most robust way to send information from my slave threads to my Main UI is to use an event structure. In this way I have my Main UI ‘subscribe’ to events from all my other threads and the event handler invokes a delegate. Each thread then raises an event any time there is information that the Main UI needs. For example:

In Main UI:

	private void HandleMotionThreadEvent(object sender, MotionThreadArgs e)
	{
		//Invoke Delegate to update display on Main UI     
		this.BeginInvoke(new UpdateMotionStateDelegate(UpdateMotionState), e.dAxis1Pos, e.enmAxis1State, e.dAxis2Pos, e.enmAxis2State);
	}

	private delegate void UpdateMotionStateDelegate (double dAxis1Pos, enmAxisState enmAxis1State, double Axis2Pos, enmAxisState enmAxis1State);
	
	private void UpdateMotionState(double dAxis1Pos, enmAxisState enmAxis1State, double Axis2Pos, enmAxisState enmAxis1State)
	{
		// Update each axis state
		XaxisStateLabel.Text = enmAxis1State.ToString();
		YaxisStateLabel.Text = enmAxis2State.ToString();
		// Update each axis Position
		XaxisPosLabel.Text = dAxis1Pos.ToString();
		YaxisPosLabel.Text = dAxis2Pos.ToString();
	}
	
	// Object containing all functionality of Motion Controller Thread 
	internal clsMotionController oMotionController { get; private set; }

	private void TopForm_Load(object sender, EventArgs e)
	{
		// Initialize the motion Controller
		oMotionController = new clsMotionController();
		  
		oMotionController.AxisStateEvent += HandleMotionThreadEvent;

		// Create and start motion Controller thread
		m_oMotionControllerThread = new Thread(oMotionController.ThreadRun);
		m_oMotionControllerThread.Name = "Motion Controller";
		m_oMotionControllerThread.Start();
	}

In Motion Thread:

	public event EventHandler<AxisStateEventArgs> AxisStateEvent;

	protected virtual void OnRaiseAxisStateEvent()
	{
		// Make a temporary copy of the event to avoid possibility of
		// a race condition if the last subscriber unsubscribes
		// immediately after the null check and before the event is raised.
		EventHandler<AxisStateEventArgs> handler = AxisStateEvent;

		// Event will be null if there are no subscribers
		if ( handler != null )
		{
			AxisStateEventArgs e = new AxisStateEventArgs (dAxis1Pos, enmAxis1State, dAxis2Pos, enmAxis2State);

			// Use the () operator to raise the event.
			handler( this, e );
		}
	}

	// These event arguments contain state information about the axes in the 
	// system used to update the user interface.
	public class AxisStateEventArgs : EventArgs
	{
		internal AxisStateEventArgs(double dAxis1Pos, enmAxisState enmAxis1State, double dAxis2Pos, enmAxisState enmAxis1State )
		{
			this.dAxis1Pos = dAxis1Pos;
			this.enmAxis1State = enmAxis1State;
			this.dAxis2Pos = dAxis2Pos;
			this.enmAxis1State = enmAxis1State;
		}
		
		public double dAxis1Pos { get; private set; }
		public enmAxisState enmAxis1State { get; private set; }
		public double dAxis2Pos { get; private set; }
		public enmAxisState enmAxis2State { get; private set; }
	}

Then in order to send an update on the Main UI anywhere in my Motion Thread I simply call this:

OnRaiseAxisStateEvent();

Additionally you can send information when raising the event that can help diagnose problems at the slave thread level, such as sending error messages or other information about when the event was sent inside the OnRaiseAxisStateEvent(). For example:

OnRaiseAxisStateEvent(strErrorMessage)

Furthermore you could set up a timer in your Motion Thread to send periodic updates to the Main UI.

Additionally you will likely need to send information from the Main UI to the other threads. In my example, I needed to set a few booleans and then send a command that I put into a class structure. This can be done using locked properties. The lock function prevents any thread from accessing that information when it is being accessed by another thread. For this reason, when the property in a slave thread is being set I set a private variable and use that variable throughout the thread.

Locked properties have to be used with caution though, because if the system is not designed properly two threads could be waiting for different properties to unlock before that thread releases the lock it currently has. Additionally if one thread stays in a state that keeps a variable locked out, and another thread is waiting for access to it, then the thread waiting for access will not continue until the lock is released. For some additional information from Microsoft regarding deadlocks click here.

In Motion Controller:

	//Public bool to exit thread
	private object m_oExitThreadLock = new Object();
	private bool m_bExitThread = false;
	
	public bool bExitThread
	{
		get
		{
			lock (m_oExitThreadLock)
			{
				return m_bExitThread;
			}
		}
		set
		{
			lock (m_oExitThreadLock)
			{
				m_bExitThread = value;
			}
		}
	}

In Main UI:

	oMotionController.bExitThread = true;

In my experience this has been a robust way to control information transfer between threads. The locked properties can get a bit tedious if you are sending a large amount of information to your slave threads, in which case it might be easier to create a class with all the information you need to send and then call an update function from your Main UI.

One quick caveat that I will mention when using these events actually caused me quite a lot of trouble. I will admit that after finding this solution I did not really dig into different combinations, or other arrays that were not objects, but when I had one event handler that I was passing an array of objects and no other information, I received a parameter mismatch error:

	private clsAxisPos[] m_oAxisPosArr;

	private void HandlePosUpdateEvent(object sender, CurrentPositionEventArgs e)
	{
		//e.oAxisPosArr was getting interpreted as 5 inputs rather than 1 object input.  This caused
		//a parameter mismatch error.  JAS of DMC 6-23-2010
		m_oAxisPosArr = e.oAxisPosArr;
		this.BeginInvoke(new UpdateCurrentPositionsDelegate( UpdateCurrentPositions ), e.oAxisPosArr);
	}

This will not work. You can have an array of objects provided you also pass other information, such as having an array of objects and an int or double or enum or really any other object, however when passing just an array of objects it is necessary to cast that array of objects into a new object in order to prevent a parameter mismatch error:

	this.BeginInvoke(new UpdateCurrentPositionsDelegate(UpdateCurrentPositions), new object[] { e.oAxisPosArr });

Hopefully this helps shed a little bit of light on one way to use delegates. I am open to any suggestions for improvement as I am always looking to improve my coding structures and styles and if you have any questions feel free to let me know.

Learn more about DMC's software and web development services.

Comments

Henry
# Henry
Thanks, awesome code, worked a treat!

Post a comment

Name (required)

Email (required)

CAPTCHA image
Enter the code shown above: