Main Site Documentation

Event interdependence


#1

Hello!

I’m wondering if there is anyone who knows if there any problems with having objects running in separate threads having a two way dependency via events being raised in either class.

Eg. one thread is responsible for recieving measurments and sending data to external hardware. Then another thread is interested in knowing about some of these measurments, so it listens to an event and does some work with the information.

Then, when the second thread has come up with some interesting results it raises an event that the first thread is listening to. So the threads have objects listening to each other via events.

I can’t see why this wouldn’t work but I’m certainly no low level expert, so if there’s anyone who can see a problem with this it would be awesome if you could object.

I know that there probably are different ways you could implement the dependencies, but I thought it would be a neat way since the stuff in the background that would cause the events is aperiodical and somewhat random.

Oh, and the events would be the only interdependence between the objects, i.e no common varable that they could change or anything like that.


#2

@ FredNgenic - can you post your code ?


#3

@ FredNgenic - I understand what you are trying do achieve. What you are describing is a classical producer/consumer pattern.

There are two types of events in .NET; Auto/Manual event objects and delegate based events.

With an AutoResetEvent or ManualResetEvent object you wait on the object, suspending the execution of the waiting thread until the event is set by another thread.

With delegate based events, you register/listen a method of an object for the event, and when the event is fired, the method of the object is called within the context of the thread that fired the event, not within the context of the thread that did the registration.


#4

Thanks for your replies.

@ VB-Daniel I would but I haven’t written it yet. =)

@ andre.m Thank you, that was what I was hoping for!

@ Mike I would be using the delegate based events, and implement a publish-subscribe pattern, but that is perhaps similar (same?) as producer/consumer pattern? My worries where whether there could be a problem if objects subscribed to each other.


#5

There should not be a problem if done correctly. Correctly means proper locking.


#6

@ FredNgenic -
Hi,
I dont understand for what you need events at all. As I understand what you want to do is, that you want have two threads, each running in a loop. Then you want to signal from one thread to the other that something has happened. I think you can write this message in a variable which the other thread reads in the loop. An eventhandler could do the same thing but I do not understand why it could do it better (at least not when there are only two threads that communicate). Furthermore, when someone reads the code of one of your classes he would not know, which of the other classes is addressed by the message, as the other class subscribes itself as recipient of the message (like NSA…)


#7

You are suggesting polling a variable instead of using an event mechanism for notifications between threads. Not the most efficient way of doing it, but it would work.

But, the OP wants to use delegate events, which do not involve two threads. They involve one thread and one or more listening objects.


#8

maybe the observer pattern is also interesting


#9

#10

God damnit, I had written a reply but it dissapeared and all I was left with was the qoute.

I’ll try again after lunch.


#11

The multiple threads is what causes the problem.

Thread 1 has Class A with an event z
Thread 2 has Class B with an event s

Class A subscribes to event s on Class B
Class B subscribes to event z on Class A.

So when Event s occurs on Class B, .net enumerates through the subscribers. But since Class A was started on thread 1, and Class B was started on thread 2, in class B’s scope there is no subscriber to it’s event s. And Class A having been started on thread 1, there is no subscriber to it’s event z.

So you need to use a delegate with your events which handles the cross thread communication.

See this http://stackoverflow.com/questions/22356/cleanest-way-to-invoke-cross-thread-events
And more info on thread safe events:
http://geekswithblogs.net/BlackRabbitCoder/archive/2011/12/01/c.net-fundamentals-safely-and-efficiently-raising-events.aspx
And info on race conditions possible with events
http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx


#12

@ mike8675309 - Delegates stored in an event are not tied to a thread. When you fire an event, the delegate method is called by the thread firing the event. If you do an invoke on a delegate, a thread from the thread poll is used.

If you use Forms or WPF, then provision has been made to do an invoke on an object with a Control base, and it will execute in the thread which created the control. There is a mechanism which is part of the GUI framework to handle this. It is not part of normal .NET.

If one thread wants to pass information to another thread, and have the other thread process it, a more elaborate mechanism is necessary, which usually involves a queue.


#13

The point I was making is that you need to use delegates for cross thread event passing. The OP is not talking about UI threads, so I am not addressing them. The 2nd link I provided provides a discussion on different ways to use delegates for cross thread event handling as you have to do it correctly to prevent unexpected results. The 3rd link points out some race conditions you might create if not careful.

Message queueing certainly would also work, but by no means is the only way to handle cross thread communication.


#14

I almost never use delegates when I need one thread to notify another thread that something has occurred and it needs to do some processing. But that’s just me. :slight_smile:


#15

@ mike8675309 - I don’t believe that this statement is correct:

When Class B raises its event, the handler in Class A is executed in the context of Class B’s thread.

The first link you provided is dealing with UI threads.

Your second two links are good references and provide good explanations of the problems using events with threads.


#16

Never mind the theory:
Here is a little console application with a main thread that starts two threads in instances of different classes. These child threads (as example) fire events when the last digit of the second of the system clock is zero (one thread) or five (the other thread).
The Program Class subscribes to the events.
How can it be achieved, that the instances of Thread_1_Class and Thread_2_Class subscribe to each others event?

[Quote] Note that the subscriber class needs a reference to the publisher class in order to subscribe to its events.
http://msdn.microsoft.com/en-us/library/ms366768.aspx
[/quote]


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace EventCommunication
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread_1_Class myThread_1_Class = new Thread_1_Class();
            myThread_1_Class.Second_0_Occurred += new Thread_1_Class.Second_0_OccurredEventHandler(myThread_1_Class_Second_5_Occurred);
           
            Thread_2_Class myThread_2_Class = new Thread_2_Class();
            myThread_2_Class.Second_5_Occurred += new Thread_2_Class.Second_5_OccurredEventHandler(myThread_2_Class_Second_5_Occurred);
        }

        static void myThread_2_Class_Second_5_Occurred(object sender, Thread_2_Class.SecondEventArgs e)
        {
            Console.WriteLine("Message from thread_2 Event occurred, argument: " + e.MySecond);
        }

        static void myThread_1_Class_Second_5_Occurred(object sender, Thread_1_Class.SecondEventArgs e)
        {
            Console.WriteLine("Message from thread_1 Event occurred, argument: " + e.MySecond);
        }
    }
    class Thread_1_Class
    {
        Thread Thread_1;
        string LastSecond = "";
        public Thread_1_Class()
        {
            Thread_1 = new Thread(new ThreadStart(Start_Thread_1));
            Thread_1.Start();
        }
        
        void Start_Thread_1()
        {
            Console.WriteLine("Hello, I have started thread_1");
            while (true)
            {
                string Second = DateTime.Now.Second.ToString();
                if ((Second != LastSecond) && (Second.EndsWith("0")))
                {
                    LastSecond = Second;
                    SecondEventArgs e = new SecondEventArgs(Second);
                    OnSecond_0_Occurred(e);
                }
                else
                {
                    LastSecond = Second;
                }
            }
        }

        public delegate void Second_0_OccurredEventHandler(object sender, SecondEventArgs e);
        public event Second_0_OccurredEventHandler Second_0_Occurred;
        void OnSecond_0_Occurred(SecondEventArgs e)
        {
            if (Second_0_Occurred != null)
                Second_0_Occurred(this, e);
        }
        public class SecondEventArgs : EventArgs
        {
            public SecondEventArgs(string Second)
            {
                MySecond = Second;
            }
            public string MySecond;
        }
    }

    class Thread_2_Class
    {
        Thread Thread_2;
        string LastSecond = "";
       
        public Thread_2_Class()
        {
            Thread_2 = new Thread(new ThreadStart(Start_Thread_2));
            Thread_2.Start();
        }
       
        void Start_Thread_2()
        {
            Console.WriteLine("Hello, I have started thread_2");
            while (true)
            {
                string Second = DateTime.Now.Second.ToString();
                if ((Second != LastSecond) && (Second.EndsWith("5")))
                {
                    LastSecond = Second;
                    SecondEventArgs e = new SecondEventArgs(Second);
                    OnSecond_5_Occurred(e);
                }
                else
                {
                    LastSecond = Second;
                }
            }
        }
        public delegate void Second_5_OccurredEventHandler(object sender, SecondEventArgs e);
        public event Second_5_OccurredEventHandler Second_5_Occurred;
        void OnSecond_5_Occurred(SecondEventArgs e)
        {
            if (Second_5_Occurred != null)
                Second_5_Occurred(this, e);
        }
        public class SecondEventArgs : EventArgs
        {
            public SecondEventArgs(string Second)
            {
                MySecond = Second;
            }
            public string MySecond;
        }
    }
}


#17

@ RoSchmi - I think this is what you are looking for:


namespace MFConsoleApplication1
{
	public class Program
	{
		public static void Main()
		{
			ClassA classA = new ClassA();
			ClassB classB = new ClassB();

			classA.FiveSecondEvent += classB.FiveSecondHandler;
			classB.TenSecondEvent += classA.TenSecondHandler;

			classA.Start();
			classB.Start();

			Thread.Sleep(60000);

			classA.Stop();
			classB.Stop();
		}
	}

	public class FiveSecondEventArgs : EventArgs
	{
		public FiveSecondEventArgs(string second)
		{
			this.FiveSecondValue = second;
		}
		public string FiveSecondValue;
	}

	public class TenSecondEventArgs : EventArgs
	{
		public TenSecondEventArgs(string second)
		{
			this.TenSecondValue = second;
		}
		public string TenSecondValue;
	}

	public delegate void FiveSecondEventHandler(object source, FiveSecondEventArgs e);
	public delegate void TenSecondEventHandler(object source, TenSecondEventArgs e);

	public class ClassA
	{
		public event FiveSecondEventHandler FiveSecondEvent;

		public void TenSecondHandler(object source, TenSecondEventArgs e)
		{
			Debug.Print("ClassA.TenSecondHandler: " + e.TenSecondValue);
		}

		public ClassA()
		{
			this.run = false;
		}

		public void Start()
		{
			this.run = true;
			this.threadA = new Thread(new ThreadStart(this.FiveSecondThread));
			this.threadA.Start();
		}

		public void Stop()
		{
			this.run = false;
			this.threadA.Join();
		}

		private void FiveSecondThread()
		{
			string lastValue = "";
			string newValue = "";
			while (this.run)
			{
				newValue = DateTime.Now.Second.ToString();
				if (newValue != lastValue && newValue.Substring(newValue.Length-1, 1) == "5")
				{
					lastValue = newValue;
					this.FiveSecondEvent(this, new FiveSecondEventArgs(newValue));
				}
				Thread.Sleep(500);
			}
		}

		private Thread threadA;
		private bool run;
	}

	public class ClassB
	{
		public event TenSecondEventHandler TenSecondEvent;

		public void FiveSecondHandler(object source, FiveSecondEventArgs e)
		{
			Debug.Print("ClassB.FiveSecondHandler: " + e.FiveSecondValue);
		}

		public ClassB()
		{
			this.run = false;
		}

		public void Start()
		{
			this.run = true;
			this.threadB = new Thread(new ThreadStart(this.TenSecondThread));
			this.threadB.Start();
		}

		public void Stop()
		{
			this.run = false;
			this.threadB.Join();
		}

		private void TenSecondThread()
		{
			string lastValue = "";
			string newValue = "";
			while (this.run)
			{
				newValue = DateTime.Now.Second.ToString();
				if (newValue != lastValue && newValue.Substring(newValue.Length - 1, 1) == "0")
				{
					lastValue = newValue;
					this.TenSecondEvent(this, new TenSecondEventArgs(newValue));
				}
				Thread.Sleep(500);
			}
		}

		private Thread threadB;
		private bool run;
	}
}

Output:
ClassB.FiveSecondHandler: 15
ClassA.TenSecondHandler: 20
ClassB.FiveSecondHandler: 25
ClassA.TenSecondHandler: 30
ClassB.FiveSecondHandler: 35
ClassA.TenSecondHandler: 40
ClassB.FiveSecondHandler: 45
ClassA.TenSecondHandler: 50
ClassB.FiveSecondHandler: 55
ClassA.TenSecondHandler: 0
ClassB.FiveSecondHandler: 5
ClassA.TenSecondHandler: 10


#18

@ jasdev -
Yes, very good.
Interesting to play around and to learn how things work.

I think , the claims that FredNgenic made are fulfilled. Unfortunately he seems to be still at lunch.

[quote]Then another thread is interested in knowing about some of these measurments, so it listens to an event and does some work with the Information.
[/quote]

[quote]Then, when the second thread has come up with some interesting results it raises an event that the first thread is listening to. So the threads have objects listening to each other via events.
[/quote]

The solution would be even better, if ClassA and ClassB could directly subscribe to the event of the other class e.g. in the constructor and if the eventargs classes could be inside of ClassA and ClassB. Then from outside the classes one would only see the calls of the constructors and anything else would work behind the curtains. But I wonder if this can be possible at all.
BTW: This all is like sending your wife a postcard every day, saying Good morning darling.
Do you think, using eventhandlers for this purpose makes sense? (I mean in the program, not to communicate with your wife)
Regards Roland


#19

@ RoSchmi - Thanks. Here is a different version, closer to what you were looking for.
This example is very simple, and all of the issues raised in @ FredNgenic’s second and third links need to fixed to make the code more robust.


namespace MFConsoleApplication1
{
	public class Program
	{
		public static void Main()
		{
			ClassA classA = new ClassA();
			ClassB classB = new ClassB();

			classA.Start(classB);
			classB.Start(classA);

			Thread.Sleep(60000);

			classA.Stop();
			classB.Stop();
		}
	}


	public class ClassA
	{
		public delegate void FiveSecondEventHandler(object source, FiveSecondEventArgs e);
		public event FiveSecondEventHandler FiveSecondEvent;

		public void TenSecondHandler(object source, ClassB.TenSecondEventArgs e)
		{
			Debug.Print("ClassA.TenSecondHandler: " + e.TenSecondValue);
		}

		public ClassA()
		{
			this.run = false;
		}

		public void Start(ClassB classB)
		{
			this.FiveSecondEvent += classB.FiveSecondHandler;

			this.run = true;
			this.threadA = new Thread(new ThreadStart(this.FiveSecondThread));
			this.threadA.Start();
		}

		public void Stop()
		{
			this.run = false;
			this.threadA.Join();
		}

		private void FiveSecondThread()
		{
			string lastValue = "";
			string newValue = "";
			while (this.run)
			{
				newValue = DateTime.Now.Second.ToString();
				if (newValue != lastValue && newValue.Substring(newValue.Length-1, 1) == "5")
				{
					lastValue = newValue;
					this.FiveSecondEvent(this, new FiveSecondEventArgs(newValue));
				}
				Thread.Sleep(500);
			}
		}

		private Thread threadA;
		private bool run;



		public class FiveSecondEventArgs : EventArgs
		{
			public FiveSecondEventArgs(string second)
			{
				this.FiveSecondValue = second;
			}

			public string FiveSecondValue;
		}
	}

	public class ClassB
	{
		public delegate void TenSecondEventHandler(object source, TenSecondEventArgs e);
		public event TenSecondEventHandler TenSecondEvent;

		public void FiveSecondHandler(object source, ClassA.FiveSecondEventArgs e)
		{
			Debug.Print("ClassB.FiveSecondHandler: " + e.FiveSecondValue);
		}

		public ClassB()
		{
			this.run = false;
		}

		public void Start(ClassA classA)
		{
			this.TenSecondEvent += classA.TenSecondHandler;

			this.run = true;
			this.threadB = new Thread(new ThreadStart(this.TenSecondThread));
			this.threadB.Start();
		}

		public void Stop()
		{
			this.run = false;
			this.threadB.Join();
		}

		private void TenSecondThread()
		{
			string lastValue = "";
			string newValue = "";
			while (this.run)
			{
				newValue = DateTime.Now.Second.ToString();
				if (newValue != lastValue && newValue.Substring(newValue.Length - 1, 1) == "0")
				{
					lastValue = newValue;
					this.TenSecondEvent(this, new TenSecondEventArgs(newValue));
				}
				Thread.Sleep(500);
			}
		}

		private Thread threadB;
		private bool run;



		public class TenSecondEventArgs : EventArgs
		{
			public TenSecondEventArgs(string second)
			{
				this.TenSecondValue = second;
			}
			public string TenSecondValue;
		}
	}
}


#20

@ jasdev - Wow! You got it.
I’m still scratching my head how an why it works. Need a free hour to have a closer look.
Regards Roland