I was playing with the Monitor class in .NET so I arrived at a piece of code that seem to be working but when I loop it for a while it throws an OutOfMemoryException
.
I am running this on a 64 bit windows 8 developer machine with 8 GB of RAM and I the process never takes up more than a 100 MB of space on the RAM.
This is my code:
using System;
using System.Threading;
public class Program
{
public static void Main()
{
while (true) {
object theLock = new Object();
Thread threadA = new Thread(() =>
{
Console.WriteLine("Thread A before lock");
lock (theLock)
{
Console.WriteLine("Thread A locking, about to Wait");
Monitor.Wait(theLock);
}
Console.WriteLine("Thread A after lock");
});
Thread threadB = new Thread(() =>
{
Console.WriteLine("Thread B before lock");
lock (theLock)
{
Console.WriteLine("Thread B lockint, about to Pulse");
Monitor.Pulse(theLock);
}
Console.WriteLine("Thread B before lock");
});
threadA.Start();
threadB.Start();
GC.Collect();
}
}
}
I read here that it might be a fragmentation problem and I added the GC.Collect()
at the end. However I am not allocating big chunks of space.
Then I decided to measure how many iterations does the loop go through approximately before it throws the exception and added a counter:
using System;
using System.Threading;
public class Program
{
public static void Main()
{
int counter = 0;
while (true) {
Console.WriteLine(counter);
counter++;
object theLock = new Object();
Thread threadA = new Thread(() =>
{
Console.WriteLine("Thread A before lock");
lock (theLock)
{
Console.WriteLine("Thread A locking, about to Wait");
Monitor.Wait(theLock);
}
Console.WriteLine("Thread A after lock");
});
Thread threadB = new Thread(() =>
{
Console.WriteLine("Thread B before lock");
lock (theLock)
{
Console.WriteLine("Thread B lockint, about to Pulse");
Monitor.Pulse(theLock);
}
Console.WriteLine("Thread B before lock");
});
threadA.Start();
threadB.Start();
GC.Collect();
}
}
}
That seems to slow down the throwing of the exception a lot. I measured 36000 iterations.
For each pair of threads, if thread A manages to acquire the lock before thread B, you'll end up with both threads completing, and everything can be cleaned up.
If thread B manages to acquire the lock before thread A, thread B will complete (having pulsed the monitor) but then thread A will acquire the monitor and wait forever for something to pulse it. So at that point you'll have:
Thread
object... all of which are tied up forever, basically.
Given that, I'm not surprised that you're seeing problems.
It's not clear what you're trying to achieve, but that explains the symptoms. Never assume that just because you call threadA.Start()
before threadB.Start()
, the first thread will actually reach a certain point in code before the second thread.
See more on this question at Stackoverflow