/// <summary> /// Raises the AfterWorkItem event /// </summary> void OnAfterWorkItem(ThreadPoolWorkItem workItem) { AfterWorkItemHandler delegateToFire; lock (this.eventLock) { delegateToFire = this.afterWorkItem; } if (delegateToFire != null) { delegateToFire(this, workItem); } }
/// <summary> /// Adds a work item to the queue and potentially start a new thread. /// A thread is started if there are no idle threads or if there is already /// something on the queue - but in each case, only if the total number of /// threads is less than the maximum. /// </summary> /// <param name="workItem">The actual work item to add to the queue.</param> public void AddWorkItem(ThreadPoolWorkItem workItem) { if (workItem == null) { throw new ArgumentNullException("workItem"); } bool startNewThread; lock (this.stateLock) { lock (this.queueLock) { if (this.queue.Count == 0) { this.queue.Enqueue(workItem); } else { // Work out where in the queue the item should go // Common case: it belongs at the end if (((ThreadPoolWorkItem)this.queue[this.queue.Count - 1]).Priority >= workItem.Priority) { this.queue.Enqueue(workItem); } else { // This will find the complement of the correct position, due to the // "interesting" nature of PriorityComparer. int position = this.queue.BinarySearch(workItem, PriorityComparer.Instance); this.queue.Enqueue(workItem, ~position); } } startNewThread = (this.WorkingThreads + this.queue.Count > this.TotalThreads) && (this.TotalThreads < this.MaxThreads); // Always pulse the queueLock, whether there's something waiting or not. // This is easier than trying to work out for sure whether or not it's // worth pulsing, and errs on the side of caution. Monitor.Pulse(this.queueLock); } } if (startNewThread) { this.StartWorkerThread(); } }
/// <summary> /// Cancels the first work item with the specified ID, if there is one. /// Note that items which have been taken off the queue and are running /// or about to be started cannot be cancelled. /// </summary> /// <param name="id">The ID of the work item to cancel</param> public bool CancelWorkItem(object id) { if (id == null) { throw new ArgumentNullException("id"); } lock (this.queueLock) { for (int i = 0; i < this.queue.Count; i++) { ThreadPoolWorkItem item = (ThreadPoolWorkItem)this.queue[i]; object otherID = item.ID; if (otherID != null && id.Equals(otherID)) { this.queue.RemoveAt(i); return(true); } } } return(false); }
/// <summary> /// Main worker thread loop. This picks jobs off the queue and executes /// them, until it's time to die. /// </summary> void WorkerThreadLoop() { // Big try/finally block just to decrement the number of threads whatever happens. try { DateTime lastJob = DateTime.UtcNow; while (true) { lock (this.stateLock) { if (this.TotalThreads > this.MaxThreads) { return; } } int waitPeriod = this.CalculateWaitPeriod(lastJob); ThreadPoolWorkItem job = this.GetNextWorkItem(waitPeriod); // No job? Check whether or not we should die if (job == null) { if (this.CheckIfThreadShouldQuit(lastJob)) { return; } } else { this.ExecuteWorkItem(job); lastJob = DateTime.UtcNow; } } } finally { this.OnWorkerThreadExit(); } }
/// <summary> /// Raises the BeforeWorkItem event /// </summary> /// <param name="workItem">The work item which is about to execute</param> /// <param name="cancel">Whether or not the work item was cancelled by an event handler</param> void OnBeforeWorkItem(ThreadPoolWorkItem workItem, out bool cancel) { cancel = false; BeforeWorkItemHandler delegateToFire; lock (this.eventLock) { delegateToFire = this.beforeWorkItem; } if (delegateToFire != null) { Delegate[] delegates = delegateToFire.GetInvocationList(); foreach (BeforeWorkItemHandler d in delegates) { d(this, workItem, ref cancel); if (cancel) { return; } } } }
/// <summary> /// Raises the WorkerException event. /// TODO: Write to the event log if no exception handlers are attached? /// </summary> void OnException(ThreadPoolWorkItem workItem, Exception e) { ThreadPoolExceptionHandler eh; lock (this.eventLock) { eh = this.exceptionHandler; } if (eh != null) { Delegate[] delegates = eh.GetInvocationList(); bool handled = false; foreach (ThreadPoolExceptionHandler d in delegates) { d(this, workItem, e, ref handled); if (handled) { return; } } } }
/// <summary> /// Executes the given work item, firing the BeforeWorkItem and AfterWorkItem events, /// and incrementing and decrementing the number of working threads. /// </summary> /// <param name="job">The work item to execute</param> void ExecuteWorkItem(ThreadPoolWorkItem job) { lock (this.stateLock) { this.workingThreads++; Thread.CurrentThread.Priority = this.workerThreadPriority; Thread.CurrentThread.IsBackground = this.workerThreadsAreBackground; } try { bool cancel; this.OnBeforeWorkItem(job, out cancel); if (cancel) { return; } try { job.Invoke(); } catch (Exception e) { this.OnException(job, e); return; } this.OnAfterWorkItem(job); } finally { lock (this.stateLock) { Thread.CurrentThread.Priority = this.workerThreadPriority; Thread.CurrentThread.IsBackground = this.workerThreadsAreBackground; this.workingThreads--; } } }