/// <summary> /// Workhorse of the JobManager. Waits until a job is assigned to it and then runs the work. /// Locks: m_ThreadStatsLock, threadlock, ThreadJob.SyncRoot /// </summary> private static void ThreadWorker() { ThreadObject me = new ThreadObject(); try { m_ReadyStack.Add(me); while (true) { if (!me.Signal.WaitOne(IdleThreadLifespan, false)) { me.Status = ThreadStatus.ReadyToDie; continue; } if (!m_Running || me.Status == ThreadStatus.Dead) { break; // quit } try { ThreadJob job = me.Job; if (job == null) // sanity check { continue; } me.Status = ThreadStatus.Running; if (job.DoWork()) { m_Completed.Enqueue(job); } } finally { me.Job = null; } } } finally { if (me.Job != null) { Console.WriteLine("Worker thread dying with non-null job!!"); } me.Job = null; } }
/// <summary> /// Assigns work to threads and keeps house. /// Locks: m_ThreadStatsLock, threadlock, ThreadJob.SyncRoot /// </summary> private static void ThreadScheduler() { try { while (true) { // move finished threads back into ready stack for (int i = m_RunningArray.Count - 1; i >= 0; i--) { if (i >= m_RunningArray.Count) { continue; } ThreadObject thread = m_RunningArray[i] as ThreadObject; // make sure the thread is alive, sanity check if (!thread.Thread.IsAlive) { if (m_Running) // dead threads in running array is expected when we're shutting down { Console.WriteLine("Dead thread found in JobManager.m_RunningArray: {0}", thread.Thread.Name); } m_RunningArray.Remove(thread); continue; } if (thread.Job == null) { m_RunningArray.Remove(thread); m_ReadyStack.Add(thread); } } // kill off threads that need to die for (int i = m_ReadyStack.Count - 1; i >= 0 && CurrentThreadCount > MinThreadCount; i--) { if (i >= m_ReadyStack.Count) { continue; } ThreadObject thread = m_ReadyStack[i] as ThreadObject; m_ReadyStack.Remove(thread); if (thread.Status == ThreadStatus.ReadyToDie) { thread.Status = ThreadStatus.Dead; thread.Signal.Set(); // wake the thread up so it can see it needs to die } } // if we're shutting down, pulse all the waiting threads so they shut down if (!m_Running) { if (m_RunningArray.Count == 0 && m_ReadyStack.Count == 0) { break; // all the workers are dead, now we die } while (m_ReadyStack.Count > 0) { ThreadObject thread = m_ReadyStack[0] as ThreadObject; thread.Status = ThreadStatus.Dead; thread.Signal.Set(); // wake the thread up so it can see it needs to die } continue; } // promote old jobs to a higher priority - no higher than High // only one promotion per priority per iteration DateTime promotion = DateTime.Now - TimeSpan.FromMilliseconds(PriorityPromotionDelay); for (int i = (int)JobPriority.Low; i < (int)JobPriority.High; i++) { if (m_Jobs[i].Count == 0) { continue; } ThreadJob job = m_Jobs[i].Peek() as ThreadJob; if (job.Enqueued < promotion) { m_Jobs[i + 1].Enqueue(m_Jobs[i].Dequeue()); job.ResetEnqueued(); } } // assign work for (int i = (int)JobPriority.Critical; i >= (int)JobPriority.Idle; i--) { while (m_ReadyStack.Count > 0 && m_Jobs[i].Count > 0) { ThreadObject thread = m_ReadyStack[m_ReadyStack.Count - 1] as ThreadObject; m_ReadyStack.Remove(thread); // don't use m_ReadyStack.Count - 1, possible to change m_RunningArray.Add(thread); thread.Job = m_Jobs[i].Dequeue() as ThreadJob; thread.Signal.Set(); // wake thread up } } // make sure we've got the minimum # of threads if (CurrentThreadCount < MinThreadCount) { Thread worker = new Thread(new ThreadStart(ThreadWorker)); worker.IsBackground = true; worker.Start(); } // see if we should create some more threads for critical tasks for (int i = 0; i < m_Jobs[(int)JobPriority.Critical].Count; i++) { Thread worker = new Thread(new ThreadStart(ThreadWorker)); worker.IsBackground = true; worker.Start(); } // lastly, create one more thread if work is getting backed up long enough if (m_Jobs[(int)JobPriority.High].Count > 0 && // make sure there's a job in the first place ((ThreadJob)m_Jobs[(int)JobPriority.High].Peek()).Enqueued < promotion && // check if it's overdue CurrentThreadCount < MaxThreadCount) // make sure we're not at max - main diff between this and promotion, crit gets new thread always { Thread worker = new Thread(new ThreadStart(ThreadWorker)); worker.IsBackground = true; worker.Start(); } Thread.Sleep(100); } } finally { m_Running = false; } }
/// <summary> /// Workhorse of the JobManager. Waits until a job is assigned to it and then runs the work. /// Locks: m_ThreadStatsLock, threadlock, ThreadJob.SyncRoot /// </summary> private static void ThreadWorker() { ThreadObject me = new ThreadObject(); try { m_ReadyStack.Add(me); while (true) { if (!me.Signal.WaitOne(IdleThreadLifespan, false)) { me.Status = ThreadStatus.ReadyToDie; continue; } if (!m_Running || me.Status == ThreadStatus.Dead) break; // quit try { ThreadJob job = me.Job; if (job == null) // sanity check continue; me.Status = ThreadStatus.Running; if (job.DoWork()) { m_Completed.Enqueue(job); } } finally { me.Job = null; } } } finally { if (me.Job != null) Console.WriteLine("Worker thread dying with non-null job!!"); me.Job = null; } }