Пример #1
0
        /// <summary>The body of the async processor to be run in a Task.  Only one should be running at a time.</summary>
        /// <remarks>This has been separated out into its own method to improve the Parallel Tasks window experience.</remarks>
        private void ConcurrentExclusiveInterleaveProcessor()
        {
            // Run while there are more tasks to be processed.  We assume that the first time through,
            // there are tasks.  If they aren't, worst case is we try to process and find none.
            bool runTasks      = true;
            bool cleanupOnExit = true;

            while (runTasks)
            {
                try
                {
                    // Process all waiting exclusive tasks
                    foreach (var task in GetExclusiveTasks())
                    {
                        _exclusiveTaskScheduler.ExecuteTask(task);

                        // Just because we executed the task doesn't mean it's "complete",
                        // if it has child tasks that have not yet completed
                        // and will complete later asynchronously.  To account for this,
                        // if a task isn't yet completed, leave the interleave processor
                        // but leave it still in a running state.  When the task completes,
                        // we'll come back in and keep going.  Note that the children
                        // must not be scheduled to this interleave, or this will deadlock.
                        if (_exclusiveProcessingIncludesChildren && !task.IsCompleted)
                        {
                            cleanupOnExit = false;
                            task.ContinueWith(_ => ConcurrentExclusiveInterleaveProcessor(), _parallelOptions.TaskScheduler);
                            return;
                        }
                    }

                    // Process all waiting concurrent tasks *until* any exclusive tasks show up, in which
                    // case we want to switch over to processing those (by looping around again).
                    System.Threading.Tasks.Parallel.ForEach(GetConcurrentTasksUntilExclusiveExists(), _parallelOptions,
                                                            ExecuteConcurrentTask);
                }
                finally
                {
                    if (cleanupOnExit)
                    {
                        lock (_internalLock)
                        {
                            // If there are no tasks remaining, we're done. If there are, loop around and go again.
                            if (_concurrentTaskScheduler.Tasks.Count == 0 && _exclusiveTaskScheduler.Tasks.Count == 0)
                            {
                                _taskExecuting = null;
                                runTasks       = false;
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Processes exclusive tasks serially until either there are no more to process
        /// or we've reached our user-specified maximum limit.
        /// </summary>
        private void ProcessExclusiveTasks()
        {
            Contract.Requires(m_processingCount == EXCLUSIVE_PROCESSING_SENTINEL, "Processing exclusive tasks requires being in exclusive mode.");
            Contract.Requires(!m_exclusiveTaskScheduler.m_tasks.IsEmpty, "Processing exclusive tasks requires tasks to be processed.");
            ContractAssertMonitorStatus(ValueLock, held: false);
            try
            {
                // Note that we're processing exclusive tasks on the current thread
                Contract.Assert(!m_threadProcessingMapping.ContainsKey(Thread.CurrentThread.ManagedThreadId),
                                "This thread should not yet be involved in this pair's processing.");
                m_threadProcessingMapping[Thread.CurrentThread.ManagedThreadId] = ProcessingMode.ProcessingExclusiveTask;

                // Process up to the maximum number of items per task allowed
                for (int i = 0; i < m_maxItemsPerTask; i++)
                {
                    // Get the next available exclusive task.  If we can't find one, bail.
                    Task exclusiveTask;
                    if (!m_exclusiveTaskScheduler.m_tasks.TryDequeue(out exclusiveTask))
                    {
                        break;
                    }

                    // Execute the task.  If the scheduler was previously faulted,
                    // this task could have been faulted when it was queued; ignore such tasks.
                    if (!exclusiveTask.IsFaulted)
                    {
                        m_exclusiveTaskScheduler.ExecuteTask(exclusiveTask);
                    }
                }
            }
            finally
            {
                // We're no longer processing exclusive tasks on the current thread
                ProcessingMode currentMode;
                m_threadProcessingMapping.TryRemove(Thread.CurrentThread.ManagedThreadId, out currentMode);
                Contract.Assert(currentMode == ProcessingMode.ProcessingExclusiveTask,
                                "Somehow we ended up escaping exclusive mode.");

                lock (ValueLock)
                {
                    // When this task was launched, we tracked it by setting m_processingCount to WRITER_IN_PROGRESS.
                    // now reset it to 0.  Then check to see whether there's more processing to be done.
                    // There might be more concurrent tasks available, for example, if concurrent tasks arrived
                    // after we exited the loop, or if we exited the loop while concurrent tasks were still
                    // available but we hit our maxItemsPerTask limit.
                    Contract.Assert(m_processingCount == EXCLUSIVE_PROCESSING_SENTINEL, "The processing mode should not have deviated from exclusive.");
                    m_processingCount = 0;
                    ProcessAsyncIfNecessary(true);
                }
            }
        }
        /// <summary>
        /// Processes concurrent tasks serially until either there are no more to process,
        /// we've reached our user-specified maximum limit, or exclusive tasks have arrived.
        /// </summary>
        private void ProcessConcurrentTasks()
        {
            Contract.Requires(m_processingCount > 0, "Processing concurrent tasks requires us to be in concurrent mode.");
            ContractAssertMonitorStatus(ValueLock, held: false);
            try
            {
                // Note that we're processing concurrent tasks on the current thread
                Contract.Assert(!m_threadProcessingMapping.ContainsKey(Thread.CurrentThread.ManagedThreadId),
                                "This thread should not yet be involved in this pair's processing.");
                m_threadProcessingMapping[Thread.CurrentThread.ManagedThreadId] = ProcessingMode.ProcessingConcurrentTasks;

                // Process up to the maximum number of items per task allowed
                for (int i = 0; i < m_maxItemsPerTask; i++)
                {
                    // Get the next available concurrent task.  If we can't find one, bail.
                    Task concurrentTask;
                    if (!m_concurrentTaskScheduler.m_tasks.TryDequeue(out concurrentTask))
                    {
                        break;
                    }

                    // Execute the task.  If the scheduler was previously faulted,
                    // this task could have been faulted when it was queued; ignore such tasks.
                    if (!concurrentTask.IsFaulted)
                    {
                        m_concurrentTaskScheduler.ExecuteTask(concurrentTask);
                    }

                    // Now check to see if exclusive tasks have arrived; if any have, they take priority
                    // so we'll bail out here.  Note that we could have checked this condition
                    // in the for loop's condition, but that could lead to extra overhead
                    // in the case where a concurrent task arrives, this task is launched, and then
                    // before entering the loop an exclusive task arrives.  If we didn't execute at
                    // least one task, we would have spent all of the overhead to launch a
                    // task but with none of the benefit.  There's of course also an inherent
                    // race condition here with regards to exclusive tasks arriving, and we're ok with
                    // executing one more concurrent task than we should before giving priority to exclusive tasks.
                    if (!m_exclusiveTaskScheduler.m_tasks.IsEmpty)
                    {
                        break;
                    }
                }
            }
            finally
            {
                // We're no longer processing concurrent tasks on the current thread
                ProcessingMode currentMode;
                m_threadProcessingMapping.TryRemove(Thread.CurrentThread.ManagedThreadId, out currentMode);
                Contract.Assert(currentMode == ProcessingMode.ProcessingConcurrentTasks,
                                "Somehow we ended up escaping concurrent mode.");

                lock (ValueLock)
                {
                    // When this task was launched, we tracked it with a positive processing count;
                    // decrement that count.  Then check to see whether there's more processing to be done.
                    // There might be more concurrent tasks available, for example, if concurrent tasks arrived
                    // after we exited the loop, or if we exited the loop while concurrent tasks were still
                    // available but we hit our maxItemsPerTask limit.
                    Contract.Assert(m_processingCount > 0, "The procesing mode should not have deviated from concurrent.");
                    if (m_processingCount > 0)
                    {
                        --m_processingCount;
                    }
                    ProcessAsyncIfNecessary(true);
                }
            }
        }
Пример #4
0
 /// <summary>Runs a concurrent task.</summary>
 /// <param name="task">The task to execute.</param>
 /// <remarks>This has been separated out into its own method to improve the Parallel Tasks window experience.</remarks>
 private void ExecuteConcurrentTask(Task task)
 {
     _concurrentTaskScheduler.ExecuteTask(task);
 }