Does Monitor.Exit yield to waiting threads?

B

bughunter

Hi,

Consider this code:

----
Monitor.Pulse(oLock);
Monitor.Exit(oLock);
----

If a thread was waiting on oLock then will the current thread
immediately yield following the call to Exit() to allow other
threads(including the thread that was waiting) to do some work? Or will
they have to wait to be handed a timeslice according to the scheduling
algorithm's normal operation?

Thus if I wanted to give the waiting thread a greater chance of
obtaining a time slice as soon as the lock is released I was thinking
of doing this:

----
Monitor.Pulse(oLock);
Monitor.Exit(oLock);
Thread.Sleep(0); // yield to another thread.
----


I realise this may be picking hairs but I have a CPU bound worker
thread that interacts closely with a GUI [thread] and to prodcue a
smoother user experience I have used Thread.Sleep(0) in a few places to
allow the GUI thread to process its message queue more often than
normal, this works very nicely. Reducing the worker thread's priority
helped a little but not as much as a few carefully placed calls to
Thread.Sleep(0). Now I have one scenario where the GUI thread is very
briefly waiting on the worker thread, and I'm wondering if I should
yield to get the absolute best resposiveness or whether this happens
anyway.

Thanks,

Colin Green
 
B

bughunter

For the record I had a think about this and came up with the following
piece of code to test what is happening:

---------------------------
class Class1
{
volatile int counter=0;
int snapshot;
object oLock = new object();

[STAThread]
static void Main(string[] args)
{
Class1 oClass1 = new Class1();
oClass1.Run();
}

public void Run()
{
Thread oThread1 = new Thread(new ThreadStart(ThreadOne));
Thread oThread2 = new Thread(new ThreadStart(ThreadTwo));

oThread1.Start();
oThread2.Start();
}

private void ThreadOne()
{
Monitor.Enter(oLock);

for(counter=0; counter<10000000; counter++)
{
if(counter==5000000)
{
Monitor.Pulse(oLock);
Monitor.Exit(oLock);
Thread.Sleep(0);
}
}
}

private void ThreadTwo()
{
// Wait for thread 1 to signal us.
Monitor.Enter(oLock);

snapshot = counter;

Monitor.Pulse(oLock);
Monitor.Exit(oLock);

Console.WriteLine("counter=" + snapshot);
}
}
---------------------------

Basically ThreadOne() counts to 10 million and releases a lock at 5
million. ThreadTwo() waits for the lock to be released and takes a
snapshot of the counter. Without the call to Thread.Sleep(0) the
snapshot value is typically around 5,000,2500
plus or minus a few hundred, just occasionally returning exactly
5,000,000.

With the call to Thread.Sleep(0) the snapshot value is always
5,000,000. I realise that without being explicitly stated in the CLR
spec or whatever that this sort behaviour may be implememtation or
platform specific, e.g. does the Mono runtime work this way? But for
now Thread.Sleep(0) works for me!

Colin.
 
B

Brian Gideon

Colin,

I've seen nothing in the documentation that indicates that Monitor.Exit
will relinquish the remainder of its time slice to other threads nor
have I observed behavior that suggests it is happening.

Brian
 
B

bughunter

Hi Brian,

Indeed, but sometimes it's unclear as to whether some action is implied
by the nature of a method. So in this particular case perhaps the
documentation should state that the current timeslice will not
explicitly be relinquished following an Exit().

Regards,

Colin.
 
B

Brian Gideon

Colin,

I'd say a lot of the time the behavior of a method is unclear because
the documentation is so coarse in some areas, especially in the area of
threading. So, I do see where you're coming from.

Brian
 
J

Jon Skeet [C# MVP]

Indeed, but sometimes it's unclear as to whether some action is implied
by the nature of a method. So in this particular case perhaps the
documentation should state that the current timeslice will not
explicitly be relinquished following an Exit().

But at that point, the class wouldn't be flexible - MS would be
*forced* never to explicitly relinquish the timeslice following a call
to Exit(), even if they later decided it would be a good idea.

There's always a very difficult balance between specifying *exactly*
what a method does, so that its behaviour can be more closely
predicted, and leaving it loose enough to allow for better alternative
implementations which still achieve the same main aim in the future.
 

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