public void Dispose()
        {
            lock (workersSync) {
                exit = true;

                foreach (WorkerWrapper worker in usedWorkers)
                {
                    worker.Dispose();
                }

                for (int i = 0; i < 5 && usedWorkers.Count > 0; i++)
                {
                    bool workerRemoved = false;
                    for (int j = usedWorkers.Count - 1; j >= 0; j--)
                    {
                        if (usedWorkers [j].IsAlive)
                        {
                            continue;
                        }

                        usedWorkers.RemoveAt(j);
                        workerRemoved = true;
                    }

                    if (workerRemoved)
                    {
                        i = 0;
                    }
                    else
                    {
                        Thread.Sleep(100);
                    }
                }

                while (freeWorkers.Count > 0)
                {
                    WorkerWrapper worker = freeWorkers.Dequeue();
                    worker.Dispose();
                }
            }

            if (distributor != null && distributor.IsAlive)
            {
                if (!distributor.Join(200))
                {
                    distributor.Abort();
                }
                distributor = null;
            }
        }
        private void DistributorThread()
        {
            while (!exit)
            {
                JobWrapper job     = null;
                bool       entered = false;
                try {
                    Monitor.Enter(jobsSync);
                    entered = true;
                    if (jobs.Count == 0)
                    {
                        Monitor.Exit(jobsSync);
                        entered = false;
                        jobEnqueued.WaitOne((int)distributorPollWait, false);
                        Monitor.Enter(jobsSync);
                        entered = true;
                        if (exit)
                        {
                            break;
                        }
                    }
                    while (jobs.Count > 0)
                    {
                        job = jobs.Peek();
                        if (!job.Aborting)
                        {
                            break;
                        }

                        job.Finish(new WorkFinishedEventArgs(ThreadState.Unstarted, null));
                        jobs.Dequeue();
                        job = null;
                    }
                } finally {
                    if (entered)
                    {
                        Monitor.Exit(jobsSync);
                    }
                }

                lock (workersSync) {
                    for (int i = usedWorkers.Count - 1; i >= 0; i--)
                    {
                        WorkerWrapper worker = usedWorkers [i];
                        if (worker.CheckFinished())
                        {
                            usedWorkers.RemoveAt(i);
                            freeWorkers.Enqueue(worker);
#if DEBUG_THREADPOOL
                            Debug.WriteLine(string.Format("{0} > Thread pool (jq:{1}, wu:{2},wf:{3}): worker finished", DateTime.Now.ToString("HH:mm:ss.ff"), jobs.Count, usedWorkers.Count,
                                                          freeWorkers.Count));
#endif
                            continue;
                        }

                        if (worker.CurrentJob.MaxRunningTime <= worker.CurrentJob.TimeStarted)
                        {
                            worker.StopWork();
                            usedWorkers.RemoveAt(i);
                            freeWorkers.Enqueue(worker);
#if DEBUG_THREADPOOL
                            Debug.WriteLine(string.Format("{0} > Thread pool (jq:{1}, wu:{2},wf:{3}): worker stopped", DateTime.Now.ToString("HH:mm:ss.ff"), jobs.Count, usedWorkers.Count,
                                                          freeWorkers.Count));
#endif
                        }

                        if (!worker.IsAlive)
                        {
                            if (worker.CurrentJob != null && !worker.CurrentJob.IsFinished)
                            {
                                worker.CurrentJob.Finish(new WorkFinishedEventArgs(ThreadState.Aborted, null));
                            }

                            usedWorkers.RemoveAt(i);
                            freeWorkers.Enqueue(worker);
#if DEBUG_THREADPOOL
                            Debug.WriteLine(string.Format("{0} > Thread pool (jq:{1}, wu:{2},wf:{3}): worker died unexpectedly", DateTime.Now.ToString("HH:mm:ss.ff"), jobs.Count, usedWorkers.Count,
                                                          freeWorkers.Count));
#endif
                        }
                    }

                    if (job != null)
                    {
                        WorkerWrapper worker = null;
                        if (freeWorkers.Count == 0)
                        {
                            if (maxWaitBeforeWorkerCreation <= (int)job.TimeEnqueued.TotalMilliseconds &&
                                usedWorkers.Count < maxWorkers && !exit)
                            {
                                worker = new WorkerWrapper(this);
#if DEBUG_THREADPOOL
                                Debug.WriteLine(string.Format("{0} > Thread pool (jq:{1}, wu:{2},wf:{3}): worker created", DateTime.Now.ToString("HH:mm:ss.ff"), jobs.Count, usedWorkers.Count,
                                                              freeWorkers.Count));
#endif
                            }
                        }
                        else
                        {
                            worker = freeWorkers.Dequeue();
                        }

                        if (worker != null)
                        {
                            if (worker.StartWork(job))
                            {
                                lock (jobsSync) {
                                    jobs.Dequeue();
#if DEBUG_THREADPOOL
                                    Debug.WriteLine(string.Format("{0} > Thread pool (jq:{1}, wu:{2},wf:{3}): job dequeued", DateTime.Now.ToString("HH:mm:ss.ff"), jobs.Count, usedWorkers.Count,
                                                                  freeWorkers.Count));
#endif
                                }
                                usedWorkers.Add(worker);
#if DEBUG_THREADPOOL
                                Debug.WriteLine(string.Format("{0} > Thread pool (jq:{1}, wu:{2},wf:{3}): worker started", DateTime.Now.ToString("HH:mm:ss.ff"), jobs.Count, usedWorkers.Count,
                                                              freeWorkers.Count));
#endif
                            }
                            else
                            {
                                freeWorkers.Enqueue(worker);
#if DEBUG_THREADPOOL
                                Debug.WriteLine(string.Format("{0} > Thread pool (jq:{1}, wu:{2},wf:{3}): worker start failed", DateTime.Now.ToString("HH:mm:ss.ff"), jobs.Count, usedWorkers.Count,
                                                              freeWorkers.Count));
#endif
                            }
                        }
                    }

                    bool workerDisposed = false;
                    do
                    {
                        if (freeWorkers.Count <= minWorkers)
                        {
                            break;
                        }

                        WorkerWrapper worker = freeWorkers.Peek();
                        if (maxWorkerSleepTime > (int)worker.TimeSleeping.TotalMilliseconds)
                        {
                            continue;
                        }

                        freeWorkers.Dequeue();
                        worker.Dispose();
                        workerDisposed = true;
#if DEBUG_THREADPOOL
                        Debug.WriteLine(string.Format("{0} > Thread pool (jq:{1}, wu:{2},wf:{3}): worker disposed", DateTime.Now.ToString("HH:mm:ss.ff"), jobs.Count, usedWorkers.Count,
                                                      freeWorkers.Count));
#endif
                    } while (workerDisposed);
                }
            }
        }