/// <summary>
        /// The worker object for the thread pool
        /// </summary>
        /// <param name="state"></param>
        private void ThreadWorker(object state)
        {
            WorkerInfo threadInfo = (WorkerInfo)state;

            do
            {
                //While there are jobs in the queue process the jobs
                while (true)
                {
                    if (threadInfo.CurrentCallBackWrapper == null)
                    {
                        KeyValuePair <QueueItemPriority, WaitCallBackWrapper> packetQueueItem;
                        lock (SyncRoot)
                        {
                            UpdateThreadWaitSleepJoinCountCache();
                            int numInJobActiveThreadsCount = Math.Max(0, threadDict.Count - CurrentNumWaitSleepJoinThreadsCache - requireJobThreadsCount);

                            if (shutdown || threadDict.Count > MaxTotalThreadsCount) //If we have too many active threads
                            {
                                //If shutdown was true then we may need to set thread to idle
                                if (threadInfo.ThreadIdle && requireJobThreadsCount > 0)
                                {
                                    requireJobThreadsCount--;
                                }

                                threadInfo.ClearThreadIdle();

                                threadDict.Remove(threadInfo.ThreadId);
                                workerInfoDict.Remove(threadInfo.ThreadId);

                                UpdateThreadWaitSleepJoinCountCache();
                                return;
                            }
                            else if (numInJobActiveThreadsCount > MaxActiveThreadsCount) //If we have too many active threads
                            {
                                //We wont close here to prevent thread creation/destruction thrashing.
                                //We will instead act as if there is no work and wait to potentially be timed out
                                if (!threadInfo.ThreadIdle)
                                {
                                    threadInfo.SetThreadIdle();
                                    requireJobThreadsCount++;
                                }

                                break;
                            }
                            else
                            {
                                //Try to get a job
                                if (!jobQueue.TryTake(out packetQueueItem)) //We fail to get a new job
                                {
                                    //If we failed to get a job we switch to idle and wait to be triggered
                                    if (!threadInfo.ThreadIdle)
                                    {
                                        threadInfo.SetThreadIdle();
                                        requireJobThreadsCount++;
                                    }

                                    break;
                                }
                                else
                                {
                                    if (threadInfo.ThreadIdle && requireJobThreadsCount > 0)
                                    {
                                        requireJobThreadsCount--;
                                    }

                                    threadInfo.UpdateCurrentCallBackWrapper(packetQueueItem.Value);
                                    threadInfo.ClearThreadIdle();
                                }
                            }
                        }
                    }

                    //Perform the waitcallBack
                    try
                    {
                        threadInfo.SetInsideCallBack();
                        threadInfo.CurrentCallBackWrapper.WaitCallBack(threadInfo.CurrentCallBackWrapper.State);
                    }
                    catch (Exception ex)
                    {
                        NetworkComms.LogError(ex, "ManagedThreadPoolCallBackError", "An unhandled exception was caught while processing a callback. Make sure to catch errors in callbacks to prevent this error file being produced.");
                    }
                    finally
                    {
                        threadInfo.ClearInsideCallBack();
                    }

                    threadInfo.UpdateLastActiveTime();
                    threadInfo.ClearCallBackWrapper();
                }

                //As soon as the queue is empty we wait until perhaps close time
                if (!threadInfo.ThreadSignal.WaitOne(250))
                {
                    //While we are waiting we check to see if we need to close
                    if (DateTime.Now - threadInfo.LastActiveTime > ThreadIdleTimeoutClose)
                    {
                        lock (SyncRoot)
                        {
                            //We have timed out but we don't go below the minimum
                            if (threadDict.Count > MinThreadsCount)
                            {
                                if (threadInfo.ThreadIdle && requireJobThreadsCount > 0)
                                {
                                    requireJobThreadsCount--;
                                }

                                threadInfo.ClearThreadIdle();

                                threadDict.Remove(threadInfo.ThreadId);
                                workerInfoDict.Remove(threadInfo.ThreadId);

                                UpdateThreadWaitSleepJoinCountCache();
                                return;
                            }
                        }
                    }
                }

                //We only leave via one of our possible breaks
            } while (true);
        }
        /// <summary>
        ///  Enqueue a callback to the thread pool.
        /// </summary>
        /// <param name="priority">The priority with which to enqueue the provided callback</param>
        /// <param name="callback">The callback to execute</param>
        /// <param name="state">The state parameter to pass to the callback when executed</param>
        /// <returns>Returns the managed threadId running the callback if one was available, otherwise -1</returns>
        public int EnqueueItem(QueueItemPriority priority, WaitCallback callback, object state)
        {
            int chosenThreadId = -1;

            lock (SyncRoot)
            {
                UpdateThreadWaitSleepJoinCountCache();

                int numInJobActiveThreadsCount = Math.Max(0, threadDict.Count - CurrentNumWaitSleepJoinThreadsCache - requireJobThreadsCount);

                //int numActiveThreads = Math.Max(0,threadDict.Count - CurrentNumWaitSleepJoinThreadsCache);
                if (!shutdown && requireJobThreadsCount == 0 && numInJobActiveThreadsCount < MaxActiveThreadsCount && threadDict.Count < MaxTotalThreadsCount)
                {
                    //Launch a new thread
                    Thread newThread = new Thread(ThreadWorker);
                    newThread.Name = "ManagedThreadPool_" + newThread.ManagedThreadId.ToString();

                    WorkerInfo info = new WorkerInfo(newThread.ManagedThreadId, new WaitCallBackWrapper(callback, state));

                    chosenThreadId = newThread.ManagedThreadId;
                    threadDict.Add(newThread.ManagedThreadId, newThread);
                    workerInfoDict.Add(newThread.ManagedThreadId, info);

                    newThread.Start(info);
                }
                else if (!shutdown && requireJobThreadsCount > 0 && numInJobActiveThreadsCount < MaxActiveThreadsCount)
                {
                    jobQueue.TryAdd(new KeyValuePair <QueueItemPriority, WaitCallBackWrapper>(priority, new WaitCallBackWrapper(callback, state)));

                    int checkCount = 0;

                    foreach (var info in workerInfoDict)
                    {
                        //Trigger the first idle thread
                        checkCount++;
                        if (info.Value.ThreadIdle)
                        {
                            info.Value.ClearThreadIdle();
                            requireJobThreadsCount--;

                            info.Value.ThreadSignal.Set();
                            chosenThreadId = info.Value.ThreadId;

                            break;
                        }

                        if (checkCount == workerInfoDict.Count)
                        {
                            throw new Exception("IdleThreads count is " + requireJobThreadsCount.ToString() + " but unable to locate thread marked as idle.");
                        }
                    }
                }
                else if (!shutdown)
                {
                    //If there are no idle threads and we can't start any new ones we just have to enqueue the item
                    jobQueue.TryAdd(new KeyValuePair <QueueItemPriority, WaitCallBackWrapper>(priority, new WaitCallBackWrapper(callback, state)));
                }
            }

            return(chosenThreadId);
        }