/// <summary>Find the next task that should be executed, based on priorities and fairness and the like.</summary> /// <param name="targetTask">The found task, or null if none was found.</param> /// <param name="queueForTargetTask"> /// The scheduler associated with the found task. Due to security checks inside of TPL, /// this scheduler needs to be used to execute that task. /// </param> private void FindNextTask_NeedsLock(out Task targetTask, out DistributedQueuedTaskSchedulerQueue queueForTargetTask) { targetTask = null; queueForTargetTask = null; // Look through each of our queue groups in sorted order. // This ordering is based on the priority of the queues. foreach (var queueGroup in _queueGroups) { var queues = queueGroup.Value; // Within each group, iterate through the queues in a round-robin // fashion. Every time we iterate again and successfully find a task, // we'll start in the next location in the group. foreach (var i in queues.CreateSearchOrder()) { queueForTargetTask = queues[i]; var items = queueForTargetTask._workItems; if (items.Count > 0) { targetTask = items.Dequeue(); if (queueForTargetTask._disposed && items.Count == 0) { RemoveQueue_NeedsLock(queueForTargetTask); } queues.NextQueueIndex = (queues.NextQueueIndex + 1) % queueGroup.Value.Count; return; } } } }
/// <summary>Initializes the debug view.</summary> /// <param name="queue">The queue to be debugged.</param> public QueuedTaskSchedulerQueueDebugView(DistributedQueuedTaskSchedulerQueue queue) { if (queue == null) { throw new ArgumentNullException("queue"); } _queue = queue; }
/// <summary>Removes a scheduler from the group.</summary> /// <param name="queue">The scheduler to be removed.</param> private void RemoveQueue_NeedsLock(DistributedQueuedTaskSchedulerQueue queue) { // Find the group that contains the queue and the queue's index within the group var queueGroup = _queueGroups[queue._priority]; var index = queueGroup.IndexOf(queue); // We're about to remove the queue, so adjust the index of the next // round-robin starting location if it'll be affected by the removal if (queueGroup.NextQueueIndex >= index) { queueGroup.NextQueueIndex--; } // Remove it queueGroup.RemoveAt(index); }
/// <summary>Creates and activates a new scheduling queue for this scheduler.</summary> /// <param name="priority">The priority level for the new queue.</param> /// <returns>The newly created and activated queue at the specified priority.</returns> public TaskScheduler ActivateNewQueue(int priority) { // Create the queue var createdQueue = new DistributedQueuedTaskSchedulerQueue(priority, this); // Add the queue to the appropriate queue group based on priority lock (_queueGroups) { QueueGroup list; if (!_queueGroups.TryGetValue(priority, out list)) { list = new QueueGroup(); _queueGroups.Add(priority, list); } list.Add(createdQueue); } // Hand the new queue back return(createdQueue); }
/// <summary> /// Process tasks one at a time in the best order. /// This should be run in a Task generated by QueueTask. /// It's been separated out into its own method to show up better in Parallel Tasks. /// </summary> private void ProcessPrioritizedAndBatchedTasks() { var continueProcessing = true; while (!_sharedCancellation.IsCancellationRequested && continueProcessing) { try { // Note that we're processing tasks on this thread _taskProcessingThread.Value = true; // Until there are no more tasks to process while (!_sharedCancellation.IsCancellationRequested) { // Try to get the next task. If there aren't any more, we're done. Task targetTask; lock (_nonthreadsafeTaskQueue) { if (_nonthreadsafeTaskQueue.Count == 0) { break; } targetTask = _nonthreadsafeTaskQueue.Dequeue(); } // If the task is null, it's a placeholder for a task in the round-robin queues. // Find the next one that should be processed. DistributedQueuedTaskSchedulerQueue queueForTargetTask = null; if (targetTask == null) { lock (_queueGroups) { FindNextTask_NeedsLock(out targetTask, out queueForTargetTask); } } // Now if we finally have a task, run it. If the task // was associated with one of the round-robin schedulers, we need to use it // as a thunk to execute its task. if (targetTask != null) { if (queueForTargetTask != null) { queueForTargetTask.ExecuteTask(targetTask); } else { TryExecuteTask(targetTask); } } } } finally { // Now that we think we're done, verify that there really is // no more work to do. If there's not, highlight // that we're now less parallel than we were a moment ago. lock (_nonthreadsafeTaskQueue) { if (_nonthreadsafeTaskQueue.Count == 0) { _delegatesQueuedOrRunning--; continueProcessing = false; _taskProcessingThread.Value = false; } } } } }