Performance Counter; .NET Locks & Threads - Current Queue Count Bu

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

During the investigation of some multithreading issues we have in our
software, I have been attempting to use the NT performance counter;
Performance Counter; .NET Locks & Threads - Current Queue Count.

I noticed that when I monitor my .NET service this value continues to rise
steadily. It is my understanding that this represents the length of the
current Wait Queue. Also monitoring the physical and logical queues shows
that these two counters stay pretty much the same. This lead me to believe
that there is a bug in the Current Queue Length counter as it is far higher
that the number of logical or physical threads.

I investigated this rising count to discover that it is a specific call to
an overloaded member Monitor.Wait(object, int), where the int is the timeout
for the wait to attempt to re-acquire a lock on the object, instead of
waiting to be pulsed indefinitely is the cause. When the timeout occurs, the
thread gets the lock again and continues, but the Current Queue Length does
not get decremented.

I wrote a small test harness to prove my theory, and when using the
Monitor.Wait(object) member, when the object is pulsed and execution
continues, the Current Queue Length is decremented.

Is this an issue with the .NET runtime Wait Queue implementation, or is it
simply a problem with the performance counter implementation?
 
Peter,

I notice the same thing. Here is the code I used.

class Test
{
private static Object _Lock = new Object();
private static int _Quantity = 0;

static void Main(string[] args)
{
Console.WriteLine("Press ENTER to go...");
Console.ReadLine();

Thread c = new Thread(new ThreadStart(Consumer));
c.IsBackground = true;
c.Start();

Thread p = new Thread(new ThreadStart(Producer));
p.IsBackground = true;
p.Start();

Console.ReadLine();
}

static void Producer()
{
while (true)
{
Monitor.Enter(_Lock);

while (_Quantity >= 10 && !Monitor.Wait(_Lock, 1));

Console.WriteLine("Producer: Quantity = " + ++_Quantity);

Monitor.Pulse(_Lock);

Monitor.Exit(_Lock);
}
}

static void Consumer()
{
while (true)
{
Monitor.Enter(_Lock);

while (_Quantity <= 0 && !Monitor.Wait(_Lock, 1));

Console.WriteLine("Consumer: Quantity = " + --_Quantity);

Monitor.Pulse(_Lock);

Monitor.Exit(_Lock);
}
}
}

As written the Current Queue Length counter (under .NET CLR
LocksAndThreads) goes up steadily. If I change the timeout from 1
millisecond to 1000 milliseconds so that the wait essentially never
times out then the counter appears normal. I'm using v1.1 of the .NET
Framework.

I think there is a bug in the counter implementation. Can someone else
confirm?

Brian
 
Hi Brian,
That's the one! Obviously the second (1000 MS) you're waiting gives your
thread scope to be pulsed. Obviously if it wasn't pulsed in this time then
the queue count would still rise. You probably know that already though.

It's very annoying because I needed to use this counter to verify our
software. When I turn round to the customer to tell them it's an MS bug.
Another garden path well exercised, and back to the drawing board ;-)

Cheers
Peter

Brian Gideon said:
Peter,

I notice the same thing. Here is the code I used.

class Test
{
private static Object _Lock = new Object();
private static int _Quantity = 0;

static void Main(string[] args)
{
Console.WriteLine("Press ENTER to go...");
Console.ReadLine();

Thread c = new Thread(new ThreadStart(Consumer));
c.IsBackground = true;
c.Start();

Thread p = new Thread(new ThreadStart(Producer));
p.IsBackground = true;
p.Start();

Console.ReadLine();
}

static void Producer()
{
while (true)
{
Monitor.Enter(_Lock);

while (_Quantity >= 10 && !Monitor.Wait(_Lock, 1));

Console.WriteLine("Producer: Quantity = " + ++_Quantity);

Monitor.Pulse(_Lock);

Monitor.Exit(_Lock);
}
}

static void Consumer()
{
while (true)
{
Monitor.Enter(_Lock);

while (_Quantity <= 0 && !Monitor.Wait(_Lock, 1));

Console.WriteLine("Consumer: Quantity = " + --_Quantity);

Monitor.Pulse(_Lock);

Monitor.Exit(_Lock);
}
}
}

As written the Current Queue Length counter (under .NET CLR
LocksAndThreads) goes up steadily. If I change the timeout from 1
millisecond to 1000 milliseconds so that the wait essentially never
times out then the counter appears normal. I'm using v1.1 of the .NET
Framework.

I think there is a bug in the counter implementation. Can someone else
confirm?

Brian
During the investigation of some multithreading issues we have in our
software, I have been attempting to use the NT performance counter;
Performance Counter; .NET Locks & Threads - Current Queue Count.

I noticed that when I monitor my .NET service this value continues to
rise steadily. It is my understanding that this represents the
length of the current Wait Queue. Also monitoring the physical and
logical queues shows that these two counters stay pretty much the
same. This lead me to believe that there is a bug in the Current
Queue Length counter as it is far higher that the number of logical
or physical threads.

I investigated this rising count to discover that it is a specific
call to an overloaded member Monitor.Wait(object, int), where the int
is the timeout for the wait to attempt to re-acquire a lock on the
object, instead of waiting to be pulsed indefinitely is the cause.
When the timeout occurs, the thread gets the lock again and
continues, but the Current Queue Length does not get decremented.

I wrote a small test harness to prove my theory, and when using the
Monitor.Wait(object) member, when the object is pulsed and execution
continues, the Current Queue Length is decremented.

Is this an issue with the .NET runtime Wait Queue implementation,
or is it simply a problem with the performance counter
implementation?
 
Peter,

I agree. The important thing here is that the performance counter is
not decremented only when the wait has timed out. I'm guessing the 1ms
wait didn't timeout all the time, but it was significantly more likely
to timeout than the 1000ms wait and that was the easiest way for me to
see the problem. Anyway, nice catch on your part. Perhaps someone
from Microsoft picked this up and logged it.

Brian
 
Back
Top