/// <summary> /// Adds a job to the JobManager. /// Locks: JobManager.m_ThreadStatsLock /// </summary> /// <param name="job">The job to enqueue.</param> /// <param name="priority">The priority of the job.</param> /// <returns>True if added, false if queue was full.</returns> internal static bool Enqueue(ThreadJob job, JobPriority priority) { if (TotalEnqueuedJobs >= QueueLengthLimit && priority != JobPriority.Critical) { try { bool print = false; using (TimedLock.Lock(m_ThreadStatsLock)) { if (m_LastError != 2) { print = true; } m_LastError = 2; } if (print) { Console.WriteLine("{0} JM Error: Rejected job because queue was full.", DateTime.Now); } } catch (Exception e) { System.Console.WriteLine("Exception Caught in JobManager code: " + e.Message); System.Console.WriteLine(e.StackTrace); } return(false); } if (TotalEnqueuedJobs >= QueueLengthLimit / 2) { try { bool print = false; using (TimedLock.Lock(m_ThreadStatsLock)) { if (m_LastError != 1) { print = true; } m_LastError = 1; } if (print) { Console.WriteLine("{0} JM Warning: Queue length exceeds half of hard limit.", DateTime.Now); } } catch (Exception e) { System.Console.WriteLine("Exception Caught in JobManager code: " + e.Message); System.Console.WriteLine(e.StackTrace); return(false); } } m_Jobs[(int)priority].Enqueue(job); return(true); }
public ThreadObject() { SyncRoot = new object(); m_Thread = Thread.CurrentThread; m_Thread.Name = String.Format("JobWorker {0}", m_NextID++); m_Job = null; m_Status = ThreadStatus.Running; m_Signal = new AutoResetEvent(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; } }
/// <summary> /// Adds a job to the JobManager. /// Locks: JobManager.m_ThreadStatsLock /// </summary> /// <param name="job">The job to enqueue.</param> /// <param name="priority">The priority of the job.</param> /// <returns>True if added, false if queue was full.</returns> internal static bool Enqueue(ThreadJob job, JobPriority priority) { if (TotalEnqueuedJobs >= QueueLengthLimit && priority != JobPriority.Critical) { try { bool print = false; using (TimedLock.Lock(m_ThreadStatsLock)) { if (m_LastError != 2) print = true; m_LastError = 2; } if (print) Console.WriteLine("{0} JM Error: Rejected job because queue was full.", DateTime.Now); } catch (Exception e) { System.Console.WriteLine("Exception Caught in JobManager code: " + e.Message); System.Console.WriteLine(e.StackTrace); } return false; } if (TotalEnqueuedJobs >= QueueLengthLimit / 2) { try { bool print = false; using (TimedLock.Lock(m_ThreadStatsLock)) { if (m_LastError != 1) print = true; m_LastError = 1; } if (print) Console.WriteLine("{0} JM Warning: Queue length exceeds half of hard limit.", DateTime.Now); } catch (Exception e) { System.Console.WriteLine("Exception Caught in JobManager code: " + e.Message); System.Console.WriteLine(e.StackTrace); return false; } } m_Jobs[(int)priority].Enqueue(job); return true; }
/// <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; } }
public static void StressCallback(ThreadJob job) { Console.WriteLine("Callback with results for job {0}", job.Results); }
public static void StressTester() { ThreadJob job = new ThreadJob(new JobWorker(StressWorker), Utility.RandomMinMax(50, 5000), new JobCompletedCallback(StressCallback)); job.Start((JobPriority)Utility.RandomMinMax(0, 4)); }