Thread Synchronization with ManualResetEvent

  • Thread starter incrediblesuperdude
  • Start date
I

incrediblesuperdude

Hi Everybody,

I have some understanding problems, about (thread) synchronization.
Hopefully somebody can explain it to me.

I have the following two processes that use the same piece of
hardware. However, they are not allowed to use it at the same time:
Process 1: System.Threading.Thread
Process 2: System.Timers.Timer elapsed event

I have taken two approaches to synchronize the access of these two
processes to the piece of hardware:
1. With Monitor.Enter(this) and Monitor.Exit(this) in a try-finally
block of the hardware code
2. With ManualResetEvent WaitOne(), Reset() and Set() also in a try-
finally block.
Both these methods could not prevent the hardware from getting called
twice.

My own theories why this doesn't work are:
1. Monitor synchronizes different Threads, but an elapsed event runs
on the same thread?
2. Two threads can both call WaitOne() at the same time, before one of
them had the chance to call Reset(). They will both then pass
WaitOne(), because the ManualResetEvent is for both still in the
signaled state.
'
I have written some test code to test situation 2 and indeed there
seems to be a possibility of no synchronization. I have posted the
code below.

Can anybody explain me why this happens, and how I can get a
synchronization for this?

Kind regards,
Gerben Heinen

### CODE ###
using System;
using System.Threading;

namespace ConsoleApplication3
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class Class1
{
public Class1(){}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Class1 c = new Class1();
c.Start();
}

private Thread thread1,thread2,thread3;
private ManualResetEvent mre = new ManualResetEvent(true);
private volatile int cntr = 0;
private Random random = new Random();

public void Start()
{
thread1 = new Thread(new ThreadStart(worker1));
thread2 = new Thread(new ThreadStart(worker2));
thread3 = new Thread(new ThreadStart(worker3));
thread1.Start();
thread2.Start();
thread3.Start();
}

private void worker1()
{
while(true)
{
try
{
Thread.Sleep(random.Next(100,1000));
mre.WaitOne();
mre.Reset();
// Console.WriteLine("worker1 started");
cntr++;
Thread.Sleep(random.Next(100,1000));
cntr--;
}
finally
{
mre.Set();
}
}
}

private void worker2()
{
while(true)
{
try
{
Thread.Sleep(random.Next(100,1000));
mre.WaitOne();
mre.Reset();
// Console.WriteLine("worker2 started");
cntr++;
Thread.Sleep(random.Next(100,1000));
cntr--;
}
finally
{
mre.Set();
}
}
}

private void worker3()
{
while(true)
{
if(cntr==2) System.Console.WriteLine("Cntr equals 2");
Thread.Sleep(10);
}
}
}
}
#######
 
P

Peter Duniho

My own theories why this doesn't work are:
1. Monitor synchronizes different Threads, but an elapsed event runs
on the same thread?

This theory is incorrect. The Timer will raise its event on a different
thread. It has no way to inject the method into an arbitrary thread.
Even if you were using the Forms.Timer class, which does always raise the
event on a specific thread, you will never see the event raised while
executing other code within that thread. It can't be executed until the
thread has returned back to the message pump loop (inside .NET).
2. Two threads can both call WaitOne() at the same time, before one of
them had the chance to call Reset(). They will both then pass
WaitOne(), because the ManualResetEvent is for both still in the
signaled state.

This is true. The code you posted has a race condition, where both
threads can get past the WaitOne() call before the other has a chance to
reset the event.

You _could_ fix it by using an AutoResetEvent instead, so that as soon as
one thread successfully passes the wait, the event is automatically reset
for you. But that's generally a more appropriate sort of thing where you
have a specific need to order access (for example, you want the threads to
strictly alternate between each other).

Not that it wouldn't work in your case either, just that it's more
complicated and unnecessarily hinders how obvious it is what the code is
doing.

If all you are trying to do is ensure two different threads aren't
accessing the same resource at the same time, then the Monitor class you
tried _is_ a correct solution. If you didn't get it to work, it's not
because the Monitor class is unsuitable.

You could also simply use the "lock()" statement, or as Michael suggests,
use a mutex.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top