Re: threading - Monitor.Wait/Pulse
- From: Casey Kramer <CaseyKramer@xxxxxxxxxxxxxxxxxxxxxxxxx>
- Date: Thu, 8 Jun 2006 06:31:03 -0700
I think the problem is that you are calling the Wait() mehod on your wait
handle within your SynLock block. The one thing Monitor.Wait does that you
don't get with WaitHandles is that it releases the Lock on the Object you
call Wait on.
I would say break it out into two SynLock groups in Thread 1, one locking
the resources just prior to calling WaitHandle.Wait(), and the other after
the Wait returns.
You'll need to limit the scope of the SynLock in Thread's 2 & 3 as well.
I'm afraid, though, that you may run into a similar problem as the one you
were experiencing with the Monitor object. You may want to consider using
the Semiphore class to allow 2 classes to access your _objTxRxSync object at
a time. You would probably also need to use either a WaitHandle, or a global
(and locked) variable to make sure that Threads 2 & 3 don't do their thing
until Thread 1 is ready. Adding the Semiphore will give you the ability to
make sure only two threads will be working with the resources at a time,
though.
Does this make any sense?
Casey
"Perecli Manole" wrote:
I had the same thought as you so I tried the AutoResetEvent. This approach.
has a strange side effect though. The WaitHandle, I'am guessing, seems to
prevent the SerialPort.DataReceived event from fireing so thread 2 and above
are not being called causing the WaitOne to always expire on the timout.
When using the Monitor.Wait function instead the event is raised again. Can
this be explained? Here a snipit that shows the basics:
'global
Private _ARE1 As New AutoResetEvent(False)
Private _ARE2 As New AutoResetEvent(False)
'---------------------- transmit tread 1 ----------------------
SyncLock _objTxRxSync
_objSerialPort.Write(objTxPacket.Bytes, 0, objCurrTxPacket.Bytes.Length)
_objTxRxSync.AssertedPacket = objTxPacket
_objTxRxSync.MatchNeeded = TxRxSync.MatchType.Echo
If _ARE1.WaitOne(PACKET_ECHO_TIMEOUT, True) Then
If objTxPacket.NeedsPacketResponse Then
_objTxRxSync.MatchNeeded = TxRxSync.MatchType.Response
_ARE2.WaitOne(PACKET_RESPONSE_TIMEOUT, True)
End If
End If
End SyncLock
'------------------receive threads 2,3,n.... ---------------------
SyncLock _objTxRxSync
If _objTxRxSync.MatchNeeded = TxRxSync.MatchType.Echo AndAlso
_objTxRxSync.AssertedPacket.Equals(_objRxPacket) Then
RaiseCallBackEvent(_objTxRxSync.AssertedPacket)
_ARE1.Set()
Exit Sub
End If
End SyncLock
'do some work that does not need syncronization for non echo packets only
'..............
SyncLock _objTxRxSync
If _objTxRxSync.MatchNeeded = TxRxSync.MatchType.Response AndAlso
_objTxRxSync.AssertedPacket.ValidPacketResponse(objRxPacket) Then
_ARE2.Set()
End If
End SyncLock
RaiseCallBackEvent(objRxPacket)
Thanks
Perry
"Casey Kramer" <CaseyKramer@xxxxxxxxxxxxxxxxxxxxxxxxx> wrote in message
news:106CF2E8-385E-4B70-8FEE-445A4B855F6B@xxxxxxxxxxxxxxxx
Well, my first suggestion might be to utilize a WaitHandle derived object
(Either a ManualResetEvent or AutoResetEvent most likely). WaitHandles
provide similar signalling to what is available with the Monitor class,
but
the fact that you can manually reset an event means that other threads
which
may call Wait on the handle will not block on that call until the handle
is
reset.
For your description, it sounds like you may want to consider creating two
WaitHandle objects, one for Thread2 and one for Thread3, then in Thread1
call
the static WaitHandle.WaitAny() passing in an array with both of those
objects. Once Thread2 or Thread3 signals thier handle (using Set()) then
Thread1 will pick back up.
WaitHandle includes WaitOne, which waits for a single WaitHandle object to
be signaled, WaitAny, which waits for any in an array of WaitHandles to be
signaled, and WaitAll, which waits for all WaitHandles in an array to be
signaled. The instances include the Set/Reset methods, but also have a
SignalAndWait method, which allows you to signal one WaitHandle, and Wait
on
another (as a single operation). It seems like some experimenting with
these
might give you the some decent results.
Looking at your original post again, it looks like this example from Juval
Lowy's "Programming .Net Components" 2nd ed. may be helpful. He calls
this a
"Rendezvous Helper", and describes it as a way to allow two threads to do
work independently, and then wait for one another to to complete before
moving on... For what it is worth here is the code (hope C# is okay):
public class Rendezvous
{
AutoResetEvent m_First = new AutoResetEvent(true);
AutoResetEvent m_Event1 = new AutoResetEvent(false);
AutoResetEvent m_Event2 = new AutoResetEvent(false);
public void Wait( )
{
bool first = m_First.WaitOne(TimeSpan.Zero,false);
if(first)
{
WaitHandle.SignalAndWait(m_Event1,m_Event2);
}
else
{
WaitHandle.SignalAndWait(m_Event2,m_Event1);
}
}
public void Reset( )
{
m_First.Set( );
}
}
Here's a usage sample:
public class RendezvousDemo
{
Rendezvous m_Rendezvous = new Rendezvous( );
public void ThreadMethod1( )
{
//Do some work, then
m_Rendezvous.Wait( );
//Continue executing
}
public void ThreadMethod2( )
{
//Do some work, then
m_Rendezvous.Wait( );
//Continue executing
}
}
RendezvousDemo demo = new RendezvousDemo( );
Thread thread1 = new Thread(demo.ThreadMethod1);
thread1.Start( );
Thread thread2 = new Thread(demo.ThreadMethod2);
thread2.Start( );
Basically what this boils down to is that thread1 and thread2 are started,
and whichever one finishes first is blocked until the other is finished,
at
which point they both continue.
I'm not sure if the example is too abstract for your needs, but hopefully
it
gives you enough to tinker with.
"Perecli Manole" wrote:
Thank you for the detail description, that explains the unpredictable
behavior I am experiencing.
But, in order for my application to work properly, I absolutely need the
waiting thread (Thread 1) to be the next thread that aquires the lock
after
either Thread 2 or 3 pulse. Thread 1 MUST be checked with after every and
any pulse from any other thread. How can I achieve this even if it has to
be
done with a different mechanism?
Thanks
Perry
"Casey Kramer" <CaseyKramer@xxxxxxxxxxxxxxxxxxxxxxxxx> wrote in message
news:FF8949ED-C2D3-4281-944E-DE31FE76BBF0@xxxxxxxxxxxxxxxx
Okay, I think I understand more of your issue now. I'll see if I can
explain
generally how Monitor Deals with Wait() and Pulse()/PulseAll() calls:
Whenever you call Monitor.Wait(), the thread that makes the call gets
put
into a Wait Queue, and releases the lock on the monitored object. At
this
point another thread may acquire the lock, and even call Wait() itself.
Once
a thread calls Pulse() on the Monitor, the first thread in the Wait
Queue
gets moved into the Lock Queue (if you use PulseAll() then all of the
threads
in the Wait Queue get moved to the lock queue). Once the thread that
called
Pulse() is done, the next item in the Lock Queue will get the lock.
I think the problem your running into comes from the fact that Thread 1
is
being put in the Lock Queue behind Thread 2 or Thread 3 (depending on
which
got the lock on the object after Thread1 called Wait()).
I think, to directly answer your question below, that after Thread 2
calls
Pulse, then Thread 3 will get the object next, followed by Thread 1.
At
least that is assuming the order of the threads is constant, which you
can't
always rely on (see my original response).
Does that make sense?
"Perecli Manole" wrote:
Could you also answer the more important last question I posed in my
last
post?
"And if thread 2 gets the resource and then releases it with a
Monitor.Pulse, will thread 3 then get the resource or will thread 1
get
it
first?"
Thanks
Perry
"Casey Kramer" <CaseyKramer@xxxxxxxxxxxxxxxxxxxxxxxxx> wrote in
message
news:F3FBD9CB-D15B-4CC8-9B4B-2BA6748A8AD6@xxxxxxxxxxxxxxxx
Since .Net Managed threads are backed by native operating system
thread
objects, then the operating system decides which thread executes in
what
order based on the time slots it has provided. The only way to
influence
the
order of execution is to muck with Thread Priorities, which is
generally
considered a Bad Thing. When Thread1 calls Wait on the monitor
object,
all
it is doing is giving up the remainder of it's time slot, and
telling
the
OS
it can switch to a different thread. The Monitor object adds items
to
a
queue whenever they call Wait(), so you can be pretty sure that the
waiting
threads will be signaled in the order they originally executed, but
you
really have no control over when a specific thread will reach that
Wait()
statement.
"Perecli Manole" wrote:
I am having some strange thread synchronization problems that
require
me
to
better understand the intricacies of Monitor.Wait/Pulse.
I have 3 threads. Thread 1 does a Monitor.Wait in a SyncLock block
protecting a resource. Thread 2 and 3 also have a SyncLock block
protecting
the same resource and after executing some code in their blocks
they
both
do
a Monitor.Pulse to hand of the locked resource back to thread 1.
While thread 1 has the resource locked both thread 2 and 3 are
blocked
at
the beginning of their SyncLock waiting for the resource to be
released.
The
question is: When thread 1 does a Monitor.Wait what determines the
order
of
which thread 2 or 3 is allowed to enter their SyncLock? And if
thread
2
gets
the resource and then releases it with a Monitor.Pulse, will thread
3
then
get the resource or will thread 1 get it first?
Thanks
Perry
- Follow-Ups:
- Re: threading - Monitor.Wait/Pulse
- From: Perecli Manole
- Re: threading - Monitor.Wait/Pulse
- From: Perecli Manole
- Re: threading - Monitor.Wait/Pulse
- References:
- threading - Monitor.Wait/Pulse
- From: Perecli Manole
- Re: threading - Monitor.Wait/Pulse
- From: Perecli Manole
- Re: threading - Monitor.Wait/Pulse
- From: Casey Kramer
- Re: threading - Monitor.Wait/Pulse
- From: Perecli Manole
- Re: threading - Monitor.Wait/Pulse
- From: Casey Kramer
- Re: threading - Monitor.Wait/Pulse
- From: Perecli Manole
- threading - Monitor.Wait/Pulse
- Prev by Date: Re: threading - Monitor.Wait/Pulse
- Next by Date: Red "X" displayed instead of controls
- Previous by thread: Re: threading - Monitor.Wait/Pulse
- Next by thread: Re: threading - Monitor.Wait/Pulse
- Index(es):
Relevant Pages
|