Im not sure this is the correct forum for this type of question, but Im currently trying to find a bug I cant reproduce in a web service using a memory dump and I think I have a specific question I need help with, that I think someone might have some input on.
Analyzing a memory dump using WinDbg I find aprox 75000 ThreadAbortExceptions in memory, and they all originate from here:
at System.Threading.WaitHandle.WaitOne(Int64 timeout Boolean exitContext)
at MyNameSpace.CustomThreadPool.Run()
They are all created in a very short period of time, when the application is trying to unload its appdomain (IIS is closing down).
What I cant figure out right now is how its possible to raise so many ThreadAbortExceptions? If a thread quits, is there any way it may raise more than one? If anyone can give any hint as to why so many exceptions of this type can exist? From what I can see there's about 20 threads max is this process, and the threadpool itself has only one(!) thread when this occurs.
The CustomThreadPool class comes from this article: http://msdn.microsoft.com/en-us/magazine/cc163851.aspx
public sealed class CustomThreadPool : IDisposable
{
private Semaphore _workWaiting;
private Queue<WaitQueueItem> _queue;
private List<Thread> _threads;
public CustomThreadPool(int numThreads)
{
if (numThreads <= 0)
throw new ArgumentOutOfRangeException("numThreads");
_threads = new List<Thread>(numThreads);
_queue = new Queue<WaitQueueItem>();
_workWaiting = new Semaphore(0, int.MaxValue);
for (int i = 0; i < numThreads; i++)
{
Thread t = new Thread(Run);
t.IsBackground = true;
_threads.Add(t);
t.Start;
}
}
public void Dispose()
{
if (_threads != null)
{
_threads.ForEach(delegate(Thread t) { t.Interrupt(); });
_threads = null;
}
}
public void QueueUserWorkItem(WaitCallback callback, object state)
{
if (_threads == null)
throw new ObjectDisposedException(GetType().Name);
if (callback == null) throw new ArgumentNullException("callback");
WaitQueueItem item = new WaitQueueItem();
item.Callback = callback;
item.State = state;
item.Context = ExecutionContext.Capture();
lock(_queue) _queue.Enqueue(item);
_workWaiting.Release();
}
private void Run()
{
try
{
while (true)
{
_workWaiting.WaitOne();
WaitQueueItem item;
lock(_queue) item = _queue.Dequeue();
ExecutionContext.Run(item.Context,
new ContextCallback(item.Callback), item.State);
}
}
catch(ThreadInterruptedException){}
}
private class WaitQueueItem
{
public WaitCallback Callback;
public object State;
public ExecutionContext Context;
}
}