Click here to Skip to main content
Click here to Skip to main content

Using events for thread synchronization

, 27 Mar 2002
Rate this:
Please Sign up or sign in to vote.
An introduction to using signaled events for thread synchronization in .NET

Introduction

Whenever you have multiple threads in your program, which is just about always, and whenever those multiple threads might have to access the same resource, which is also a very probable contingency, you will need to incorporate some kind of thread synchronization technique. There are threads that will only access a resource in a read-only manner. Let's call them ReadThreads and then there are threads that will write to a resource. We call them WriteThreads or at least let's call them that for now. If a thread reads and writes a shared resource it will still be a WriteThread. The sample application is a windows forms application created using C# and which has three buttons, one for each case. You need to try out each button to see what happens in each of the cases we discuss below.

Case 1 - No synchronization

Alright let's imagine a situation where we have two ReadThreads that run in parallel and also access a shared object. In addition there is also a WriteThread that starts before the ReadThreads and which sets a valid value for the shared object. I have used Thread.Sleep to simulate processing time in the sample code snippets below.

Thread t0 = new Thread(new ThreadStart(WriteThread));
Thread t1 = new Thread(new ThreadStart(ReadThread10));
Thread t2 = new Thread(new ThreadStart(ReadThread20));
t0.IsBackground=true;
t1.IsBackground=true;
t2.IsBackground=true;
t0.Start();
t1.Start();
t2.Start();

As you can see, we have started the WriteThread and then immediately started our two ReadThreads. I'll show the code snippets for these functions below:-

public void WriteThread()
{
	Thread.Sleep(1000);
	m_x=3;
}	
public void ReadThread10()
{
	int a = 10;
	for(int y=0;y<5;y++)
	{
		string s = "ReadThread10";
		s = s + " # multiplier= ";
		s = s + Convert.ToString(a) + " # ";
		s = s + a * m_x;
		listBox1.Items.Add(s);
		Thread.Sleep(1000);
	}
}
public void ReadThread20()
{
	int a = 20;
	for(int y=0;y<5;y++)
	{
		string s = "ReadThread20";
		s = s + " # multiplier= ";
		s = s + Convert.ToString(a) + " # ";
		s = s + a * m_x;
		listBox1.Items.Add(s);
		Thread.Sleep(1000);
	}
}

When we run the program, we get the output shown below :-

Aha! So we have got the first two values wrong, one from each thread. What happened was that the ReadThreads started executing before the WriteThread had finished it's job. This is a totally unwanted situation and we should surely try and do something to avoid this.

Case 2 - Synchronization [One WriteThread - Many ReadThreads]

Now we are going to solve the problem we faced in Case 1. We'll use the ManualResetEvent thread synchronization class. As before we start the WriteThread and the two ReadThreads. The only difference is that we use safe versions of these threads.

Thread t0 = new Thread(new ThreadStart(SafeWriteThread));
Thread t1 = new Thread(new ThreadStart(SafeReadThread10));
Thread t2 = new Thread(new ThreadStart(SafeReadThread20));
t0.IsBackground=true;
t1.IsBackground=true;
t2.IsBackground=true;
t0.Start();
t1.Start();
t2.Start();

We also add a ManualResetEvent object to our class.

public ManualResetEvent m_mre;

We initialize it in our class's constructor.

m_mre = new ManualResetEvent(false);

Now let's look at out SafeWriteThread function

public void SafeWriteThread()
{
	m_mre.Reset();
	WriteThread();
	m_mre.Set();
}

The Reset function sets the state of the event object to non-signaled. This means the event is currently not set. Then we call the original WriteThread function. Actually we could have skipped the Reset step because we had set the state to non-signaled in the ManualResetEvent constructor earlier. Once the WriteThread function returns we call the Set function which will set the state of the event object to signaled. Now the event is said to be set.

Now, let's take a look at out two SafeReadThread functions.

public void SafeReadThread10()
{
	m_mre.WaitOne();
	ReadThread10();
}
public void SafeReadThread20()
{
	m_mre.WaitOne();
	ReadThread20();
}

The WaitOne function will block till the event object's signaled state is set. Thus in our particular scenario, both the SafeReadThreads will block till the event object is signaled. Our SafeWriteThread will set the event only after it has done it's job. Thus we ensure that the reading threads start reading the shared resource only after the writing thread has done it's job. Now when we run the program we get this output which is what we wanted to get.

Voila! Perfecto!

Case 3 - Synchronization [Many WriteThreads - Many ReadThreads]

Now assume we have a situation where we have two WriteThreads. Now the ReadThreads would have to wait till all the WriteThreads have finished their work. In a real scenario, both the WriteThreads would probably be running together, but in our example I've run them in a serialized order where the the second WriteThread starts only after the first one has finished. This is only for ease of simulation. In our case since the second WriteThread starts only after the first WriteThread the ReadThreads need to only wait on the second thread, but as I already said, simply imagine that the two WriteThreads are running in parallel.

We add another ManualResetEvent object for the second thread and also an array of ManualResetEvent objects.

public ManualResetEvent m_mreB;
public ManualResetEvent[] m_mre_array;

Now we add the following initialization code in our constructor

m_mreB = new ManualResetEvent(false);
m_mre_array = new ManualResetEvent[2];
m_mre_array[0]=m_mre;
m_mre_array[1]=m_mreB;

Now lets see how we start the four threads

Thread t0 = new Thread(new ThreadStart(SafeWriteThread));
Thread t0B = new Thread(new ThreadStart(SafeWriteThreadB));
Thread t1 = new Thread(new ThreadStart(SafeReadThread10B));
Thread t2 = new Thread(new ThreadStart(SafeReadThread20B));
t0.IsBackground=true;
t0B.IsBackground=true;
t1.IsBackground=true;
t2.IsBackground=true;
t0.Start();
t0B.Start();
t1.Start();
t2.Start();

As you can see, we now have two StartThreads and two WriteThreads. Lets see their implementations.

public void SafeWriteThread()
{
	m_mre.Reset();
	WriteThread();
	m_mre.Set();
}

As you can see, SafeWriteThread is same as before.

public void SafeWriteThreadB()
{	
	m_mreB.Reset();
	m_mre.WaitOne();
	Thread.Sleep(1000);
	m_x+=3;			
	m_mreB.Set();
}

Well, as you can see we have used another event object for this second WriteThread. For the sake of simulation there is a wait for the first thread to finish its work, but as mentioned before this is not a true representation of the real life state of affairs.

public void SafeReadThread10B()
{
	WaitHandle.WaitAll(m_mre_array);
	ReadThread10();
}

public void SafeReadThread20B()
{
	WaitHandle.WaitAll(m_mre_array);
	ReadThread20();
}

As you can see we have used a function called WaitAll. It's a static member function of the WaitHandle class which is the base class for our ManualResetEvent class. The function takes in an array of WaitHandle objects to which we pass our ManualResetEvent object array. The casting is implicitly done as we are casting to a parent class. What WaitHandle does is this. It will block till each object in the array has been put into a signaled state or in other words till every object in the array has been set. When we run the program this is what we get.

Cool, huh? It worked nice and fine.

AutoResetEvent

There is a very similar class called AutoResetEvent. The difference from the ManualResetEvent class is that the AutoResetEvent is automatically reset to non-signaled after any waiting thread has been released. The best I could figure out for the purpose of this class is this. Lets assume we have several threads waiting for access to an object. We don't want all of them to get access together. So when we are ready to allow access to one thread, we set the event object they are all waiting on. This object will be an AutoResetEvent object. Now one of the threads is released, but the moment that happens, the event is non-signaled automatically. Thus the other threads will continue to wait till the main thread or the thread that is accessing the event object decides to set the event to a signaled state.

I have put together a simple console application to demonstrate this class.

class Class1
{
	AutoResetEvent m_are;
	static void Main(string[] args)
	{
		Class1 class1 = new Class1();			

	}

	Class1()
	{
		m_are = new AutoResetEvent (false);
		Thread t1 = new Thread(new ThreadStart(abc));
		Thread t2 = new Thread(new ThreadStart(xyz));
		t1.Start();
		t2.Start();			
		m_are.Set();
		Thread.Sleep(3000);
		m_are.Set();
	}

	void abc()
	{
		m_are.WaitOne();
		for(int i=0;i<5;i++)
		{
			Thread.Sleep(1000);
			Console.WriteLine("abc abc abc");
		}
	}

	void xyz()
	{
		m_are.WaitOne();
		for(int i=0;i<5;i++)
		{
			Thread.Sleep(1000);
			Console.WriteLine("xyz xyz xyz");
		}
	}
}

When we run the above program we get something like this as output.

abc abc abc
abc abc abc
abc abc abc
xyz xyz xyz
abc abc abc
abc abc abc
xyz xyz xyz
xyz xyz xyz
xyz xyz xyz
xyz xyz xyz

Conclusion

This essay is not a comprehensive one in the sense it does not detail each and every little nuance associated with the thread synchronization event classes. But I do hope it gives you a start from where you can reach out to taller heights or perhaps the expression should be reach down to even more profound depths.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Nish Sivakumar

United States United States
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
 
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site - www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff - blog.voidnish.com.
 
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy Summer Love and Some more Cricket as well as a programming book – Extending MFC applications with the .NET Framework.
 
Nish's latest book C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
 
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.

Comments and Discussions

 
QuestionMy vote of 5 Pinmembersam34401-Aug-12 9:19 
QuestionCase 2: is it possible that a shared resource is altered by a writer while readers are accessing it, because the writer is not blocked after all? Pinmemberwilsonsuryajaya2-May-12 18:14 
QuestionSyntax error? Pinmemberavtuvy22-Feb-11 9:44 
AnswerRe: Syntax error? PinmvpNishant Sivakumar22-Feb-11 9:47 
GeneralRe: Syntax error? Pinmemberavtuvy22-Feb-11 10:15 
GeneralRe: Syntax error? PinmvpNishant Sivakumar22-Feb-11 10:32 
AnswerRe: Syntax error? Pinmemberavtuvy22-Feb-11 10:07 
GeneralRe: Syntax error? PinmvpNishant Sivakumar22-Feb-11 10:33 
Yeah, that's right.

QuestionDid This Ever Work? PinmemberJohn Pittaway16-Jun-10 13:25 
AnswerRe: Did This Ever Work? PinmvpNishant Sivakumar16-Jun-10 15:19 
AnswerRe: Did This Ever Work? PinmvpNishant Sivakumar16-Jun-10 15:43 
Generaldatabase access using Synchronous Threading PinmemberJan Palmer4-Sep-07 3:03 
GeneralWow this really helps thanks Pinmemberoci_Beken14-Aug-07 17:11 
General..thanks PinmemberAeyd M.2-Jun-06 20:25 
GeneralRe: ..thanks PinmemberChristian Klauser12-Aug-06 5:07 
GeneralRe: ..thanks [modified] Pinmembererkan islamovic16-Oct-06 23:19 
QuestionHow to call a device driver from a VC++ Program? PinmemberKirubanandam27-Aug-05 3:22 
GeneralAutoResetEvent-example Pinmemberpat2708818-Aug-04 11:11 
Generalyes, yes, yes... i'll have what he's having! PinmemberNik Vogiatzis20-Jul-04 15:08 
GeneralIt seems can not specify the event name Pinmemberkosmas22-Sep-03 4:22 
GeneralReaderWriterLock & Monitor PinmemberBlake Coverett28-Mar-02 17:16 
General[Message Deleted] PinmemberNish [BusterBoy]28-Mar-02 17:34 
GeneralRe: ReaderWriterLock & Monitor PinmemberBlake Coverett28-Mar-02 18:36 
GeneralRe: ReaderWriterLock & Monitor PinmemberNish [BusterBoy]28-Mar-02 18:44 
GeneralRe: ReaderWriterLock & Monitor PinmemberTim Smith30-Mar-02 14:27 
GeneralRe: ReaderWriterLock & Monitor PinmemberBlake Coverett30-Mar-02 15:07 
GeneralRe: ReaderWriterLock & Monitor PinmemberTim Smith30-Mar-02 15:55 
GeneralRe: ReaderWriterLock &amp; Monitor PinmemberNish [BusterBoy]30-Mar-02 17:10 
GeneralRe: ReaderWriterLock & Monitor PinmemberTim Smith31-Mar-02 2:34 
GeneralRe: ReaderWriterLock & Monitor PinmemberBlake Coverett31-Mar-02 6:49 
General[Message Deleted] PinmemberNish [BusterBoy]30-Mar-02 17:06 
GeneralRe: ReaderWriterLock & Monitor PinmemberWilliam E. Kempf1-Apr-02 4:07 
GeneralRe: ReaderWriterLock & Monitor PinmemberTim Smith30-Mar-02 13:50 
GeneralRe: ReaderWriterLock & Monitor PinmemberBlake Coverett30-Mar-02 14:39 
GeneralRe: ReaderWriterLock & Monitor PinmemberTim Smith30-Mar-02 16:01 
GeneralRe: ReaderWriterLock & Monitor PinmemberWilliam E. Kempf1-Apr-02 4:24 
GeneralRe: ReaderWriterLock & Monitor PinmemberTim Smith1-Apr-02 11:04 
GeneralRe: ReaderWriterLock & Monitor PinmemberJoao Vaz1-Apr-02 11:24 
GeneralRe: ReaderWriterLock & Monitor PinmemberTim Smith1-Apr-02 11:59 
GeneralRe: ReaderWriterLock & Monitor PinmemberTim Smith1-Apr-02 12:01 
GeneralRe: ReaderWriterLock & Monitor PinmemberJoao Vaz1-Apr-02 22:08 
GeneralRe: ReaderWriterLock & Monitor PinmemberWilliam E. Kempf1-Apr-02 12:18 
GeneralRe: ReaderWriterLock & Monitor PinmemberTim Smith1-Apr-02 12:40 
GeneralRe: I am sorry... PinmemberTim Smith1-Apr-02 12:31 
GeneralRe: I am sorry... PinmemberWilliam E. Kempf1-Apr-02 12:35 
GeneralRe: I am sorry... PinmemberJoao Vaz1-Apr-02 22:33 
GeneralRe: I am sorry... PinmemberTim Smith2-Apr-02 2:05 
GeneralRe: I am sorry... PinmemberJoao Vaz2-Apr-02 2:46 
GeneralRe: I am sorry... PinmemberWilliam E. Kempf2-Apr-02 8:19 
GeneralRe: I am sorry... PinmemberTim Smith2-Apr-02 9:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 28 Mar 2002
Article Copyright 2002 by Nish Sivakumar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid