TaskScheduler TaskScheduler TaskScheduler TaskScheduler Class

定義

表示物件,這個物件會處理將工作排入至執行緒上的低階工作。Represents an object that handles the low-level work of queuing tasks onto threads.

public ref class TaskScheduler abstract
public abstract class TaskScheduler
type TaskScheduler = class
Public MustInherit Class TaskScheduler
繼承
TaskSchedulerTaskSchedulerTaskSchedulerTaskScheduler

範例

下列範例取自使用.NET Framework 4 進行平行程式設計的範例MSDN Code Gallery 網站上。The following example is taken from the Samples for Parallel Programming with the .NET Framework 4 on the MSDN Code Gallery Web site. 它會建立自訂工作排程器,以限制應用程式所使用的執行緒數目。It creates a custom task scheduler that limits the number of threads used by the app. 然後啟動兩組工作,並顯示工作和工作執行所在之執行緒的相關資訊。It then launches two sets of tasks and displays information about the task and the thread on which the task is executing.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

class Example
{
   static void Main()
   {
       // Create a scheduler that uses two threads. 
       LimitedConcurrencyLevelTaskScheduler lcts = new LimitedConcurrencyLevelTaskScheduler(2);
       List<Task> tasks = new List<Task>();
       
       // Create a TaskFactory and pass it our custom scheduler. 
       TaskFactory factory = new TaskFactory(lcts);
       CancellationTokenSource cts = new CancellationTokenSource();
       
       // Use our factory to run a set of tasks. 
       Object lockObj = new Object();
       int outputItem = 0;
       
       for (int tCtr = 0; tCtr <= 4; tCtr++) {
          int iteration = tCtr;
          Task t = factory.StartNew(() => {
                                       for (int i = 0; i < 1000; i++) {
                                          lock (lockObj) {
                                             Console.Write("{0} in task t-{1} on thread {2}   ", 
                                                           i, iteration, Thread.CurrentThread.ManagedThreadId);
                                             outputItem++;
                                             if (outputItem % 3 == 0)
                                                Console.WriteLine();
                                          }
                                       }                   
                                    }, cts.Token);
          tasks.Add(t);                      
      }
      // Use it to run a second set of tasks.                       
      for (int tCtr = 0; tCtr <= 4; tCtr++) {
         int iteration = tCtr;
         Task t1 = factory.StartNew(() => {
                                       for (int outer = 0; outer <= 10; outer++) {
                                          for (int i = 0x21; i <= 0x7E; i++) {
                                             lock (lockObj) {
                                                Console.Write("'{0}' in task t1-{1} on thread {2}   ", 
                                                              Convert.ToChar(i), iteration, Thread.CurrentThread.ManagedThreadId);
                                                outputItem++;
                                                if (outputItem % 3 == 0)
                                                   Console.WriteLine();
                                             } 
                                          }
                                       }                                           
                                    }, cts.Token);           
         tasks.Add(t1);
      }
      
      // Wait for the tasks to complete before displaying a completion message.
      Task.WaitAll(tasks.ToArray());
      cts.Dispose();
      Console.WriteLine("\n\nSuccessful completion.");
   }
}

// Provides a task scheduler that ensures a maximum concurrency level while 
// running on top of the thread pool.
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
   // Indicates whether the current thread is processing work items.
   [ThreadStatic]
   private static bool _currentThreadIsProcessingItems;

  // The list of tasks to be executed 
   private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)

   // The maximum concurrency level allowed by this scheduler. 
   private readonly int _maxDegreeOfParallelism;

   // Indicates whether the scheduler is currently processing work items. 
   private int _delegatesQueuedOrRunning = 0;

   // Creates a new instance with the specified degree of parallelism. 
   public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
   {
       if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
       _maxDegreeOfParallelism = maxDegreeOfParallelism;
   }

   // Queues a task to the scheduler. 
   protected sealed override void QueueTask(Task task)
   {
      // Add the task to the list of tasks to be processed.  If there aren't enough 
      // delegates currently queued or running to process tasks, schedule another. 
       lock (_tasks)
       {
           _tasks.AddLast(task);
           if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
           {
               ++_delegatesQueuedOrRunning;
               NotifyThreadPoolOfPendingWork();
           }
       }
   }

   // Inform the ThreadPool that there's work to be executed for this scheduler. 
   private void NotifyThreadPoolOfPendingWork()
   {
       ThreadPool.UnsafeQueueUserWorkItem(_ =>
       {
           // Note that the current thread is now processing work items.
           // This is necessary to enable inlining of tasks into this thread.
           _currentThreadIsProcessingItems = true;
           try
           {
               // Process all available items in the queue.
               while (true)
               {
                   Task item;
                   lock (_tasks)
                   {
                       // When there are no more items to be processed,
                       // note that we're done processing, and get out.
                       if (_tasks.Count == 0)
                       {
                           --_delegatesQueuedOrRunning;
                           break;
                       }

                       // Get the next item from the queue
                       item = _tasks.First.Value;
                       _tasks.RemoveFirst();
                   }

                   // Execute the task we pulled out of the queue
                   base.TryExecuteTask(item);
               }
           }
           // We're done processing items on the current thread
           finally { _currentThreadIsProcessingItems = false; }
       }, null);
   }

   // Attempts to execute the specified task on the current thread. 
   protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
   {
       // If this thread isn't already processing a task, we don't support inlining
       if (!_currentThreadIsProcessingItems) return false;

       // If the task was previously queued, remove it from the queue
       if (taskWasPreviouslyQueued) 
          // Try to run the task. 
          if (TryDequeue(task)) 
            return base.TryExecuteTask(task);
          else
             return false; 
       else 
          return base.TryExecuteTask(task);
   }

   // Attempt to remove a previously scheduled task from the scheduler. 
   protected sealed override bool TryDequeue(Task task)
   {
       lock (_tasks) return _tasks.Remove(task);
   }

   // Gets the maximum concurrency level supported by this scheduler. 
   public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } }

   // Gets an enumerable of the tasks currently scheduled on this scheduler. 
   protected sealed override IEnumerable<Task> GetScheduledTasks()
   {
       bool lockTaken = false;
       try
       {
           Monitor.TryEnter(_tasks, ref lockTaken);
           if (lockTaken) return _tasks;
           else throw new NotSupportedException();
       }
       finally
       {
           if (lockTaken) Monitor.Exit(_tasks);
       }
   }
}
// The following is a portion of the output from a single run of the example:
//    'T' in task t1-4 on thread 3   'U' in task t1-4 on thread 3   'V' in task t1-4 on thread 3   
//    'W' in task t1-4 on thread 3   'X' in task t1-4 on thread 3   'Y' in task t1-4 on thread 3   
//    'Z' in task t1-4 on thread 3   '[' in task t1-4 on thread 3   '\' in task t1-4 on thread 3   
//    ']' in task t1-4 on thread 3   '^' in task t1-4 on thread 3   '_' in task t1-4 on thread 3   
//    '`' in task t1-4 on thread 3   'a' in task t1-4 on thread 3   'b' in task t1-4 on thread 3   
//    'c' in task t1-4 on thread 3   'd' in task t1-4 on thread 3   'e' in task t1-4 on thread 3   
//    'f' in task t1-4 on thread 3   'g' in task t1-4 on thread 3   'h' in task t1-4 on thread 3   
//    'i' in task t1-4 on thread 3   'j' in task t1-4 on thread 3   'k' in task t1-4 on thread 3   
//    'l' in task t1-4 on thread 3   'm' in task t1-4 on thread 3   'n' in task t1-4 on thread 3   
//    'o' in task t1-4 on thread 3   'p' in task t1-4 on thread 3   ']' in task t1-2 on thread 4   
//    '^' in task t1-2 on thread 4   '_' in task t1-2 on thread 4   '`' in task t1-2 on thread 4   
//    'a' in task t1-2 on thread 4   'b' in task t1-2 on thread 4   'c' in task t1-2 on thread 4   
//    'd' in task t1-2 on thread 4   'e' in task t1-2 on thread 4   'f' in task t1-2 on thread 4   
//    'g' in task t1-2 on thread 4   'h' in task t1-2 on thread 4   'i' in task t1-2 on thread 4   
//    'j' in task t1-2 on thread 4   'k' in task t1-2 on thread 4   'l' in task t1-2 on thread 4   
//    'm' in task t1-2 on thread 4   'n' in task t1-2 on thread 4   'o' in task t1-2 on thread 4   
//    'p' in task t1-2 on thread 4   'q' in task t1-2 on thread 4   'r' in task t1-2 on thread 4   
//    's' in task t1-2 on thread 4   't' in task t1-2 on thread 4   'u' in task t1-2 on thread 4   
//    'v' in task t1-2 on thread 4   'w' in task t1-2 on thread 4   'x' in task t1-2 on thread 4   
//    'y' in task t1-2 on thread 4   'z' in task t1-2 on thread 4   '{' in task t1-2 on thread 4   
//    '|' in task t1-2 on thread 4   '}' in task t1-2 on thread 4   '~' in task t1-2 on thread 4   
//    'q' in task t1-4 on thread 3   'r' in task t1-4 on thread 3   's' in task t1-4 on thread 3   
//    't' in task t1-4 on thread 3   'u' in task t1-4 on thread 3   'v' in task t1-4 on thread 3   
//    'w' in task t1-4 on thread 3   'x' in task t1-4 on thread 3   'y' in task t1-4 on thread 3   
//    'z' in task t1-4 on thread 3   '{' in task t1-4 on thread 3   '|' in task t1-4 on thread 3  
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Sub Main()
      ' Create a scheduler that uses two threads. 
      Dim lcts As New LimitedConcurrencyLevelTaskScheduler(2)
      Dim tasks As New List(Of Task)()
      
      ' Create a TaskFactory and pass it our custom scheduler. 
      Dim factory As New TaskFactory(lcts)
      Dim cts As New CancellationTokenSource()
      
      ' Use our factory to run a set of tasks. 
      Dim objLock As New Object()      
      Dim outputItem As Integer 
      For tCtr As Integer = 0 To 4
         Dim iteration As Integer = tCtr
         Dim t As Task = factory.StartNew(Sub()
                                             For i As Integer = 1 To 1000
                                                SyncLock objLock
                                                   Console.Write("{0} in task t-{1} on thread {2}   ", 
                                                   i, iteration, Thread.CurrentThread.ManagedThreadId)
                                                   outputItem += 1
                                                   If outputItem Mod 3 = 0 Then Console.WriteLine()
                                                End SyncLock
                                             Next 
                                          End Sub,
                                cts.Token)
         tasks.Add(t)
      Next 
      ' Use it to run a second set of tasks.                       
      For tCtr As Integer = 0 To 4
         Dim iteration As Integer = tCtr
         Dim t1 As Task = factory.StartNew(Sub()
                                              For outer As Integer = 0 To 10
                                                 For i As Integer = &h21 To &h7E
                                                    SyncLock objLock
                                                       Console.Write("'{0}' in task t1-{1} on thread {2}   ", 
                                                                     Convert.ToChar(i), iteration, Thread.CurrentThread.ManagedThreadId)
                                                       outputItem += 1
                                                       If outputItem Mod 3 = 0 Then Console.WriteLine()
                                                    End SyncLock 
                                                 Next     
                                              Next                                           
                                           End Sub,
                                cts.Token)           
         tasks.Add(t1)
      Next
      
      ' Wait for the tasks to complete before displaying a completion message.
      Task.WaitAll(tasks.ToArray())
      cts.Dispose()
      Console.WriteLine(vbCrLf + vbCrLf + "Successful completion.")
   End Sub 
End Module

' Provides a task scheduler that ensures a maximum concurrency level while 
' running on top of the thread pool.
Public Class LimitedConcurrencyLevelTaskScheduler : Inherits TaskScheduler
   ' Indicates whether the current thread is processing work items.
   <ThreadStatic()> Private Shared _currentThreadIsProcessingItems As Boolean 
   
   ' The list of tasks to be executed 
   Private ReadOnly _tasks As LinkedList(Of Task) = New LinkedList(Of Task)() 
   
   'The maximum concurrency level allowed by this scheduler. 
   Private ReadOnly _maxDegreeOfParallelism As Integer 
   
   ' Indicates whether the scheduler is currently processing work items. 
   Private _delegatesQueuedOrRunning As Integer = 0 ' protected by lock(_tasks)
   
   ' Creates a new instance with the specified degree of parallelism. 
   Public Sub New(ByVal maxDegreeOfParallelism As Integer)
      If (maxDegreeOfParallelism < 1) Then 
         Throw New ArgumentOutOfRangeException("maxDegreeOfParallelism")
      End If
         _maxDegreeOfParallelism = maxDegreeOfParallelism
   End Sub 

   ' Queues a task to the scheduler. 
   Protected Overrides Sub QueueTask(ByVal t As Task)
      ' Add the task to the list of tasks to be processed.  If there aren't enough 
      ' delegates currently queued or running to process tasks, schedule another. 
      SyncLock (_tasks)
         _tasks.AddLast(t)
         If (_delegatesQueuedOrRunning < _maxDegreeOfParallelism) Then
            _delegatesQueuedOrRunning = _delegatesQueuedOrRunning + 1
            NotifyThreadPoolOfPendingWork()
         End If 
      End SyncLock 
   End Sub 
   
   ' Inform the ThreadPool that there's work to be executed for this scheduler. 
   Private Sub NotifyThreadPoolOfPendingWork()
   
      ThreadPool.UnsafeQueueUserWorkItem(Sub()
                                            ' Note that the current thread is now processing work items. 
                                            ' This is necessary to enable inlining of tasks into this thread.
                                            _currentThreadIsProcessingItems = True 
                                            Try 
                                               ' Process all available items in the queue. 
                                               While (True)
                                                  Dim item As Task
                                                  SyncLock (_tasks)
                                                     ' When there are no more items to be processed, 
                                                     ' note that we're done processing, and get out. 
                                                     If (_tasks.Count = 0) Then
                                                        _delegatesQueuedOrRunning = _delegatesQueuedOrRunning - 1
                                                        Exit While 
                                                     End If 
   
                                                     ' Get the next item from the queue
                                                     item = _tasks.First.Value
                                                     _tasks.RemoveFirst()
                                                  End SyncLock 
   
                                                  ' Execute the task we pulled out of the queue 
                                                  MyBase.TryExecuteTask(item)
                                               End While 
                                               ' We're done processing items on the current thread 
                                            Finally
                                               _currentThreadIsProcessingItems = False 
                                            End Try 
                                         End Sub,
                                    Nothing)
   End Sub 
   
   ' Attempts to execute the specified task on the current thread. 
   Protected Overrides Function TryExecuteTaskInline(ByVal t As Task, 
                                                     ByVal taskWasPreviouslyQueued As Boolean) As Boolean 
      ' If this thread isn't already processing a task, we don't support inlining 
      If (Not _currentThreadIsProcessingItems) Then 
         Return False 
      End If 
   
      ' If the task was previously queued, remove it from the queue 
      If (taskWasPreviouslyQueued) Then
         ' Try to run the task. 
         If TryDequeue(t) Then 
            Return MyBase.TryExecuteTask(t)
         Else
            Return False 
         End If     
      Else 
         Return MyBase.TryExecuteTask(t)
      End If   
   End Function 
   
   ' Attempt to remove a previously scheduled task from the scheduler. 
   Protected Overrides Function TryDequeue(ByVal t As Task) As Boolean 
      SyncLock (_tasks)
         Return _tasks.Remove(t)
      End SyncLock 
   End Function 
   
   ' Gets the maximum concurrency level supported by this scheduler. 
   Public Overrides ReadOnly Property MaximumConcurrencyLevel As Integer 
      Get 
         Return _maxDegreeOfParallelism
      End Get 
   End Property 
   
   ' Gets an enumerable of the tasks currently scheduled on this scheduler. 
   Protected Overrides Function GetScheduledTasks() As IEnumerable(Of Task)
      Dim lockTaken As Boolean = False 
      Try
         Monitor.TryEnter(_tasks, lockTaken)
         If (lockTaken) Then 
            Return _tasks.ToArray()
         Else 
            Throw New NotSupportedException()
         End If 
      Finally 
         If (lockTaken) Then
            Monitor.Exit(_tasks)
         End If 
      End Try 
   End Function 
End Class 
' The following is a portion of the output from a single run of the example:
'    'T' in task t1-4 on thread 3   'U' in task t1-4 on thread 3   'V' in task t1-4 on thread 3   
'    'W' in task t1-4 on thread 3   'X' in task t1-4 on thread 3   'Y' in task t1-4 on thread 3   
'    'Z' in task t1-4 on thread 3   '[' in task t1-4 on thread 3   '\' in task t1-4 on thread 3   
'    ']' in task t1-4 on thread 3   '^' in task t1-4 on thread 3   '_' in task t1-4 on thread 3   
'    '`' in task t1-4 on thread 3   'a' in task t1-4 on thread 3   'b' in task t1-4 on thread 3   
'    'c' in task t1-4 on thread 3   'd' in task t1-4 on thread 3   'e' in task t1-4 on thread 3   
'    'f' in task t1-4 on thread 3   'g' in task t1-4 on thread 3   'h' in task t1-4 on thread 3   
'    'i' in task t1-4 on thread 3   'j' in task t1-4 on thread 3   'k' in task t1-4 on thread 3   
'    'l' in task t1-4 on thread 3   'm' in task t1-4 on thread 3   'n' in task t1-4 on thread 3   
'    'o' in task t1-4 on thread 3   'p' in task t1-4 on thread 3   ']' in task t1-2 on thread 4   
'    '^' in task t1-2 on thread 4   '_' in task t1-2 on thread 4   '`' in task t1-2 on thread 4   
'    'a' in task t1-2 on thread 4   'b' in task t1-2 on thread 4   'c' in task t1-2 on thread 4   
'    'd' in task t1-2 on thread 4   'e' in task t1-2 on thread 4   'f' in task t1-2 on thread 4   
'    'g' in task t1-2 on thread 4   'h' in task t1-2 on thread 4   'i' in task t1-2 on thread 4   
'    'j' in task t1-2 on thread 4   'k' in task t1-2 on thread 4   'l' in task t1-2 on thread 4   
'    'm' in task t1-2 on thread 4   'n' in task t1-2 on thread 4   'o' in task t1-2 on thread 4   
'    'p' in task t1-2 on thread 4   'q' in task t1-2 on thread 4   'r' in task t1-2 on thread 4   
'    's' in task t1-2 on thread 4   't' in task t1-2 on thread 4   'u' in task t1-2 on thread 4   
'    'v' in task t1-2 on thread 4   'w' in task t1-2 on thread 4   'x' in task t1-2 on thread 4   
'    'y' in task t1-2 on thread 4   'z' in task t1-2 on thread 4   '{' in task t1-2 on thread 4   
'    '|' in task t1-2 on thread 4   '}' in task t1-2 on thread 4   '~' in task t1-2 on thread 4   
'    'q' in task t1-4 on thread 3   'r' in task t1-4 on thread 3   's' in task t1-4 on thread 3   
'    't' in task t1-4 on thread 3   'u' in task t1-4 on thread 3   'v' in task t1-4 on thread 3   
'    'w' in task t1-4 on thread 3   'x' in task t1-4 on thread 3   'y' in task t1-4 on thread 3   
'    'z' in task t1-4 on thread 3   '{' in task t1-4 on thread 3   '|' in task t1-4 on thread 3  

此外,數個範例工作排程器可在程式碼庫上:使用.NET Framework 4 進行平行程式設計的範例In addition, several sample task schedulers are available on Code Gallery: Samples for Parallel Programming with the .NET Framework 4.

備註

執行個體TaskScheduler類別代表工作排程器。An instance of the TaskScheduler class represents a task scheduler. 工作排程器可確保最終會執行工作 (Task) 的工作 (Work)。A task scheduler ensures that the work of a task is eventually executed.

預設工作排程器是以 .NET Framework 4 執行緒集區為基礎,其可提供工作竊取 (以達到平衡負載)、執行緒插入/停用 (以達到最大輸送量),以及整體良好效能。The default task scheduler is based on the .NET Framework 4 thread pool, which provides work-stealing for load-balancing, thread injection/retirement for maximum throughput, and overall good performance. 這應該足以應付大多數的情況。It should be sufficient for most scenarios.

TaskScheduler類別也可做為所有可自訂的排程邏輯的擴充點。The TaskScheduler class also serves as the extension point for all customizable scheduling logic. 這包括機制,例如如何排程執行的工作,以及如何排定的工作應該公開給偵錯工具。This includes mechanisms such as how to schedule a task for execution, and how scheduled tasks should be exposed to debuggers. 如果您需要特殊的功能,您可以建立自訂排程器,並啟用針對特定工作或查詢。If you require special functionality, you can create a custom scheduler and enable it for specific tasks or queries.

本主題內容:In this topic:
預設工作排程器和執行緒集區The default task scheduler and the thread pool
全域佇列與本機佇列The global queue vs. local queues
工作竊取Work stealing
長時間執行的工作Long-running tasks
內嵌工作Task inlining
指定同步處理內容Specifying a synchronization context

預設工作排程器和執行緒集區The default task scheduler and the thread pool

工作平行程式庫和 PLINQ 的預設排程器會使用.NET Framework 執行緒集區,由ThreadPool類別,以排入佇列,並執行工作。The default scheduler for the Task Parallel Library and PLINQ uses the .NET Framework thread pool, which is represented by the ThreadPool class, to queue and execute work. 執行緒集區會使用所提供的資訊Task有效率地支援細部平行處理原則 (存留較短的工作單位) 的平行工作和查詢通常代表的型別。The thread pool uses the information that is provided by the Task type to efficiently support the fine-grained parallelism (short-lived units of work) that parallel tasks and queries often represent.

全域佇列與本機佇列The global queue vs. local queues

執行緒集區會維護全域 FIFO (先進先出) 工作在每個應用程式定義域中為執行緒的佇列。The thread pool maintains a global FIFO (first-in, first-out) work queue for threads in each application domain. 每當程式呼叫ThreadPool.QueueUserWorkItem(或ThreadPool.UnsafeQueueUserWorkItem) 方法,工作放入這個共用佇列,最後再從佇列到下一個可用的執行緒。Whenever a program calls the ThreadPool.QueueUserWorkItem (or ThreadPool.UnsafeQueueUserWorkItem) method, the work is put on this shared queue and eventually de-queued onto the next thread that becomes available. 從.NET Framework 4 開始,此佇列已改良為使用類似的無鎖定演算法ConcurrentQueue<T>類別。Starting with the .NET Framework 4, this queue has been improved to use a lock-free algorithm that resembles the ConcurrentQueue<T> class. 藉由使用這種無鎖定實作,執行緒集區會花較少的時間,放入或移出佇列的工作項目。By using this lock-free implementation, the thread pool spends less time when it queues and de-queues work items. 此效能優點是能夠使用執行緒集區的所有程式。This performance benefit is available to all programs that use the thread pool.

如同任何其他的工作 (Work) 項目,最上層工作 (Task) (也就是不是在其他工作 (Task) 的內容中建立的工作 (Task)) 會放入全域佇列中。Top-level tasks, which are tasks that are not created in the context of another task, are put on the global queue just like any other work item. 不過,巢狀工作或子工作 (也就是在其他工作的內容中建立的工作) 的處理方式則相當不同。However, nested or child tasks, which are created in the context of another task, are handled quite differently. 子工作或巢狀工作會放入執行父工作的執行緒專屬的本機佇列中。A child or nested task is put on a local queue that is specific to the thread on which the parent task is executing. 父工作可以是最上層工作,也可以是其他工作的子工作。The parent task may be a top-level task or it also may be the child of another task. 這個執行緒在準備好要處理更多工作時,會先查看本機佇列。When this thread is ready for more work, it first looks in the local queue. 如果本機佇列中有待處理的工作項目,則可以快速存取這些工作項目。If work items are waiting there, they can be accessed quickly. 本機佇列存取後進先出順序 (LIFO) 來保留快取位置,並減少爭用情形。The local queues are accessed in last-in, first-out order (LIFO) to preserve cache locality and reduce contention. 如需子工作和巢狀的工作的詳細資訊,請參閱附加與中斷連結子工作For more information about child tasks and nested tasks, see Attached and Detached Child Tasks.

使用本機佇列不僅可減少全域佇列中,不足的壓力,也會利用資料位置。The use of local queues not only reduces pressure on the global queue, but also takes advantage of data locality. 在本機的工作項目排入佇列經常在記憶體中實際上是彼此接近的參考資料結構。Work items in the local queue frequently reference data structures that are physically near one another in memory. 在這些情況下,資料已經在快取之後的第一個工作已經執行,而且可以快速存取。In these cases, the data is already in the cache after the first task has run and can be accessed quickly. 兩者平行 LINQ (PLINQ)Parallel廣泛,類別使用巢狀工作和子工作,並使用本機工作佇列達到顯著的加速效果。Both Parallel LINQ (PLINQ) and the Parallel class use nested tasks and child tasks extensively, and achieve significant speedups by using the local work queues.

工作竊取Work stealing

從.NET Framework 4 開始,執行緒集區也提供工作竊取演算法,可協助您確認沒有任何執行緒會有某些閒置而其他人仍有工作在其佇列。Starting with the .NET Framework 4, the thread pool also features a work-stealing algorithm to help make sure that no threads are sitting idle while others still have work in their queues. 執行緒集區的執行緒在準備好要處理更多工作時,會先查看自己本機佇列的開頭,接著查看全域佇列,然後再查看其他執行緒的本機佇列。When a thread-pool thread is ready for more work, it first looks at the head of its local queue, then in the global queue, and then in the local queues of other threads. 如果在其他執行緒的本機佇列中發現了工作項目,它會先套用啟發學習法,確定可有效率地執行工作。If it finds a work item in the local queue of another thread, it first applies heuristics to make sure that it can run the work efficiently. 如果可以它會移出佇列工作中的項目 (以 FIFO 順序) 的結尾。If it can, it de-queues the work item from the tail (in FIFO order). 這樣可以減少本機佇列爭用的情形發生,並保留資料位置。This reduces contention on each local queue and preserves data locality. 這個架構會讓執行緒集區進行負載平衡工作比舊版更有效率。This architecture helps the thread pool load-balance work more efficiently than past versions did.

長時間執行的工作Long-running tasks

您可以明確防止將工作放入本機佇列中。You may want to explicitly prevent a task from being put on a local queue. 例如,您可能知道某個工作項目會執行相當長的一段時間,而可能阻礙本機佇列上所有其他工作項目的進度。For example, you may know that a particular work item will run for a relatively long time and is likely to block all other work items on the local queue. 在這種情況下,您可以指定 System.Threading.Tasks.TaskCreationOptions 選項,提示排程器可能需要再加入一個執行緒來執行工作,才不會阻礙本機佇列上其他執行緒或工作項目的進度。In this case, you can specify the System.Threading.Tasks.TaskCreationOptions option, which provides a hint to the scheduler that an additional thread might be required for the task so that it does not block the forward progress of other threads or work items on the local queue. 使用此選項您避免在執行緒集區完全,包括全域和本機佇列。By using this option you avoid the thread pool completely, including the global and local queues.

內嵌工作Task inlining

在某些情況下,當等候 Task 時,您可以在執行等候作業的執行緒上以同步方式執行它。In some cases when a Task is waited on, it may be executed synchronously on the thread that is performing the wait operation. 這樣可以提升效能,因為可以改為使用現有的執行緒,而不需要使用會被封鎖的額外執行緒。This enhances performance by preventing the need for an additional thread and instead using the existing thread, which would have blocked otherwise. 為防止因重新進入而導致的錯誤,只有當在相關執行緒的本機佇列中發現等候目標時,才會發生工作內嵌。To prevent errors due to reentrancy, task inlining only occurs when the wait target is found in the relevant thread's local queue.

指定同步處理內容Specifying a synchronization context

您可以使用 TaskScheduler.FromCurrentSynchronizationContext 方法,指定應該將工作排定在特定執行緒上執行。You can use the TaskScheduler.FromCurrentSynchronizationContext method to specify that a task should be scheduled to run on a particular thread. 在 Windows Form 和 Windows Presentation Foundation 等架構中,只有在當初建立使用者介面物件的執行緒上執行的程式碼能夠存取該 UI 物件,因此很適合使用這個方法。This is useful in frameworks such as Windows Forms and Windows Presentation Foundation where access to user interface objects is often restricted to code that is running on the same thread on which the UI object was created.

下列範例會使用TaskScheduler.FromCurrentSynchronizationContext排程的工作建立使用者介面 (UI) 控制項的相同執行緒上的 Windows Presentation Foundation (WPF) 應用程式中的方法。The following example uses the TaskScheduler.FromCurrentSynchronizationContext method in a Windows Presentation Foundation (WPF) app to schedule a task on the same thread that the user interface (UI) control was created on. 此範例會建立 mosaic 從指定的目錄中隨機選取的映像。The example creates a mosaic of images that are randomly selected from a specified directory. WPF 物件會用來載入和調整影像大小。The WPF objects are used to load and resize the images. 未經處理的像素接著會傳遞至工作會使用For迴圈,以像素資料寫入大型的單一位元組陣列。The raw pixels are then passed to a task that uses a For loop to write the pixel data into a large single-byte array. 任何同步處理不是必要的因為沒有任何兩個磚佔據相同的陣列項目。No synchronization is required because no two tiles occupy the same array elements. 圖格也依照任何順序寫入,因為它們的位置計算獨立於任何其他的圖格。The tiles can also be written in any order because their position is calculated independently of any other tile. 大型的陣列然後傳遞給像素資料載入影像控制項的位置在 UI 執行緒執行的工作。The large array is then passed to a task that runs on the UI thread, where the pixel data is loaded into an Image control.

此範例會將在 UI 執行緒外的資料移動、 修改使用平行迴圈和Task物件,然後再將它傳遞回 UI 執行緒執行的工作。The example moves data off the UI thread, modifies it by using parallel loops and Task objects, and then passes it back to a task that runs on the UI thread. 當您必須使用工作平行程式庫來執行作業,不支援由 WPF API,或不夠快,則這個方法會很有用。This approach is useful when you have to use the Task Parallel Library to perform operations that either are not supported by the WPF API, or are not sufficiently fast. 在 WPF 中建立的映像 mosaic 的另一種方式是使用System.Windows.Controls.WrapPanel控制項,並將影像加入至它。Another way to create an image mosaic in WPF is to use a System.Windows.Controls.WrapPanel control and add images to it. WrapPanel處理定位的圖格的工作。The WrapPanel handles the work of positioning the tiles. 不過,這項工作只可以在 UI 執行緒上執行。However, this work can only be performed on the UI thread.

using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace WPF_CS1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private int fileCount;
        int colCount;
        int rowCount;
        private int tilePixelHeight;
        private int tilePixelWidth;
        private int largeImagePixelHeight;
        private int largeImagePixelWidth;
        private int largeImageStride;
        PixelFormat format;
        BitmapPalette palette = null;

        public MainWindow()
        {
            InitializeComponent();

            // For this example, values are hard-coded to a mosaic of 8x8 tiles.
            // Each tile is 50 pixels high and 66 pixels wide and 32 bits per pixel.
            colCount = 12;
            rowCount = 8;
            tilePixelHeight = 50;
            tilePixelWidth = 66;
            largeImagePixelHeight = tilePixelHeight * rowCount;
            largeImagePixelWidth = tilePixelWidth * colCount;
            largeImageStride = largeImagePixelWidth * (32 / 8);
            this.Width = largeImagePixelWidth + 40;
            image.Width = largeImagePixelWidth;
            image.Height = largeImagePixelHeight;


        }

        private void button_Click(object sender, RoutedEventArgs e)
        {

            // For best results use 1024 x 768 jpg files at 32bpp.
            string[] files = System.IO.Directory.GetFiles(@"C:\Users\Public\Pictures\Sample Pictures\", "*.jpg");

            fileCount = files.Length;
            Task<byte[]>[] images = new Task<byte[]>[fileCount];
            for (int i = 0; i < fileCount; i++)
            {
                int x = i;
                images[x] = Task.Factory.StartNew(() => LoadImage(files[x]));
            }

            // When they've all been loaded, tile them into a single byte array.
            var tiledImage = Task.Factory.ContinueWhenAll(
                images, (i) => TileImages(i));

            // We are currently on the UI thread. Save the sync context and pass it to
            // the next task so that it can access the UI control "image".
            var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();

            // On the UI thread, put the bytes into a bitmap and
            // display it in the Image control.
            var t3 = tiledImage.ContinueWith((antecedent) =>
            {
                // Get System DPI.
                Matrix m = PresentationSource.FromVisual(Application.Current.MainWindow)
                                            .CompositionTarget.TransformToDevice;
                double dpiX = m.M11;
                double dpiY = m.M22;

                BitmapSource bms = BitmapSource.Create(largeImagePixelWidth,
                    largeImagePixelHeight,
                    dpiX,
                    dpiY,
                    format,
                    palette, //use default palette
                    antecedent.Result,
                    largeImageStride);
                image.Source = bms;
            }, UISyncContext);
        }

        byte[] LoadImage(string filename)
        {
            // Use the WPF BitmapImage class to load and 
            // resize the bitmap. NOTE: Only 32bpp formats are supported correctly.
            // Support for additional color formats is left as an exercise
            // for the reader. For more information, see documentation for ColorConvertedBitmap.

            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.UriSource = new Uri(filename);
            bitmapImage.DecodePixelHeight = tilePixelHeight;
            bitmapImage.DecodePixelWidth = tilePixelWidth;
            bitmapImage.EndInit();

            format = bitmapImage.Format;
            int size = (int)(bitmapImage.Height * bitmapImage.Width);
            int stride = (int)bitmapImage.Width * 4;
            byte[] dest = new byte[stride * tilePixelHeight];

            bitmapImage.CopyPixels(dest, stride, 0);

            return dest;
        }

        int Stride(int pixelWidth, int bitsPerPixel)
        {
            return (((pixelWidth * bitsPerPixel + 31) / 32) * 4);
        }

        // Map the individual image tiles to the large image
        // in parallel. Any kind of raw image manipulation can be
        // done here because we are not attempting to access any 
        // WPF controls from multiple threads.
        byte[] TileImages(Task<byte[]>[] sourceImages)
        {
            byte[] largeImage = new byte[largeImagePixelHeight * largeImageStride];
            int tileImageStride = tilePixelWidth * 4; // hard coded to 32bpp

            Random rand = new Random();
            Parallel.For(0, rowCount * colCount, (i) =>
            {
                // Pick one of the images at random for this tile.
                int cur = rand.Next(0, sourceImages.Length);
                byte[] pixels = sourceImages[cur].Result;

                // Get the starting index for this tile.
                int row = i / colCount;
                int col = (int)(i % colCount);
                int idx = ((row * (largeImageStride * tilePixelHeight)) + (col * tileImageStride));

                // Write the pixels for the current tile. The pixels are not contiguous
                // in the array, therefore we have to advance the index by the image stride
                // (minus the stride of the tile) for each scanline of the tile.
                int tileImageIndex = 0;
                for (int j = 0; j < tilePixelHeight; j++)
                {
                    // Write the next scanline for this tile.
                    for (int k = 0; k < tileImageStride; k++)
                    {
                        largeImage[idx++] = pixels[tileImageIndex++];
                    }
                    // Advance to the beginning of the next scanline.
                    idx += largeImageStride - tileImageStride;
                }
            });
            return largeImage;
        }
    }
}
Imports System.Threading.Tasks
Imports System.Windows
Imports System.Windows.Media
Imports System.Windows.Media.Imaging

Partial Public Class MainWindow : Inherits Window
    Dim fileCount As Integer
    Dim colCount As Integer
    Dim rowCount As Integer
    Dim tilePixelHeight As Integer
    Dim tilePixelWidth As Integer
    Dim largeImagePixelHeight As Integer
    Dim largeImagePixelWidth As Integer
    Dim largeImageStride As Integer
    Dim format As PixelFormat
    Dim palette As BitmapPalette = Nothing

    Public Sub New()
        InitializeComponent()

        ' For this example, values are hard-coded to a mosaic of 8x8 tiles.
        ' Each tile Is 50 pixels high and 66 pixels wide and 32 bits per pixel.
        colCount = 12
        rowCount = 8
        tilePixelHeight = 50
        tilePixelWidth = 66
        largeImagePixelHeight = tilePixelHeight * rowCount
        largeImagePixelWidth = tilePixelWidth * colCount
        largeImageStride = largeImagePixelWidth * (32 / 8)
        Me.Width = largeImagePixelWidth + 40
        image.Width = largeImagePixelWidth
        image.Height = largeImagePixelHeight
    End Sub

    Private Sub button_Click(sender As Object, e As RoutedEventArgs) _
        Handles button.Click

        ' For best results use 1024 x 768 jpg files at 32bpp.
        Dim files() As String = System.IO.Directory.GetFiles("C:\Users\Public\Pictures\Sample Pictures\", "*.jpg")

        fileCount = files.Length
        Dim images(fileCount - 1) As Task(Of Byte())
        For i As Integer = 0 To fileCount - 1
            Dim x As Integer = i
            images(x) = Task.Factory.StartNew(Function() LoadImage(files(x)))
        Next

        ' When they have all been loaded, tile them into a single byte array.
        'var tiledImage = Task.Factory.ContinueWhenAll(
        '        images, (i) >= TileImages(i));

        '        Dim tiledImage As Task(Of Byte()) = Task.Factory.ContinueWhenAll(images, Function(i As Task(Of Byte())) TileImages(i))
        Dim tiledImage = Task.Factory.ContinueWhenAll(images, Function(i As Task(Of Byte())()) TileImages(i))
        ' We are currently on the UI thread. Save the sync context and pass it to
        ' the next task so that it can access the UI control "image1".
        Dim UISyncContext = TaskScheduler.FromCurrentSynchronizationContext()

        ' On the UI thread, put the bytes into a bitmap and
        ' display it in the Image control.
        Dim t3 = tiledImage.ContinueWith(Sub(antecedent)
                                             ' Get System DPI.
                                             Dim m As Matrix = PresentationSource.FromVisual(Application.Current.MainWindow).CompositionTarget.TransformToDevice
                                             Dim dpiX As Double = m.M11
                                             Dim dpiY As Double = m.M22

                                             ' Use the default palette in creating the bitmap.
                                             Dim bms As BitmapSource = BitmapSource.Create(largeImagePixelWidth,
                                                                                           largeImagePixelHeight,
                                             dpiX,
                                             dpiY,
                                             format,
                                             palette,
                                             antecedent.Result,
                                             largeImageStride)
                                             image.Source = bms
                                         End Sub, UISyncContext)
    End Sub

    Public Function LoadImage(filename As String) As Byte()
        ' Use the WPF BitmapImage class to load and 
        ' resize the bitmap. NOTE: Only 32bpp formats are supported correctly.
        ' Support for additional color formats Is left as an exercise
        ' for the reader. For more information, see documentation for ColorConvertedBitmap.
        Dim bitmapImage As New BitmapImage()
        bitmapImage.BeginInit()
        bitmapImage.UriSource = New Uri(filename)
        bitmapImage.DecodePixelHeight = tilePixelHeight
        bitmapImage.DecodePixelWidth = tilePixelWidth
        bitmapImage.EndInit()

        format = bitmapImage.Format
        Dim size As Integer = CInt(bitmapImage.Height * bitmapImage.Width)
        Dim stride As Integer = CInt(bitmapImage.Width * 4)
        Dim dest(stride * tilePixelHeight - 1) As Byte

        bitmapImage.CopyPixels(dest, stride, 0)

        Return dest
    End Function

    Function Stride(pixelWidth As Integer, bitsPerPixel As Integer) As Integer
        Return (((pixelWidth * bitsPerPixel + 31) / 32) * 4)
    End Function

    ' Map the individual image tiles to the large image
    ' in parallel. Any kind of raw image manipulation can be
    ' done here because we are Not attempting to access any 
    ' WPF controls from multiple threads.
    Function TileImages(sourceImages As Task(Of Byte())()) As Byte()
        Dim largeImage(largeImagePixelHeight * largeImageStride - 1) As Byte
        Dim tileImageStride As Integer = tilePixelWidth * 4 ' hard coded To 32bpp

        Dim rand As New Random()
        Parallel.For(0, rowCount * colCount, Sub(i)
                                                 ' Pick one of the images at random for this tile.
                                                 Dim cur As Integer = rand.Next(0, sourceImages.Length)
                                                 Dim pixels() As Byte = sourceImages(cur).Result

                                                 ' Get the starting index for this tile.
                                                 Dim row As Integer = i \ colCount
                                                 Dim col As Integer = i Mod colCount
                                                 Dim idx As Integer = ((row * (largeImageStride * tilePixelHeight)) + (col * tileImageStride))

                                                 ' Write the pixels for the current tile. The pixels are Not contiguous
                                                 ' in the array, therefore we have to advance the index by the image stride
                                                 ' (minus the stride of the tile) for each scanline of the tile.
                                                 Dim tileImageIndex As Integer = 0
                                                 For j As Integer = 0 To tilePixelHeight - 1
                                                     ' Write the next scanline for this tile.
                                                     For k As Integer = 0 To tileImageStride - 1
                                                         largeImage(idx) = pixels(tileImageIndex)
                                                         idx += 1
                                                         tileImageIndex += 1
                                                     Next
                                                     ' Advance to the beginning of the next scanline.
                                                     idx += largeImageStride - tileImageStride
                                                 Next
                                             End Sub)
        Return largeImage
    End Function
End Class

若要建立範例,在 Visual Studio 中建立 WPF 應用程式專案並將它命名為 WPF_CS1 (如C#的 WPF 專案) 或 WPF_VB1 (適用於 Visual Basic WPF 專案)。To create the example, create a WPF application project in Visual Studio and name it WPF_CS1 (for a C# WPF project) or WPF_VB1 (for a Visual Basic WPF project). 然後執行下列作業:Then do the following:

  1. 在 [設計] 檢視中,拖曳Image控制項從工具箱拖曳至設計介面的左上角。In design view, drag an Image control from the Toolbox onto the upper left corner of the design surface. 名稱的文字方塊屬性視窗中,命名控制 「 映像 」。In the Name textbox of the Properties window, name the control "image".

  2. 拖曳Button控制項從工具箱到應用程式視窗左側的下半部。Drag a Button control from the Toolbox to the lower left part of the application window. 在 [XAML] 檢視中,指定Content屬性為"Make mosaic"按鈕,並指定其Width屬性設為"100"。In XAML view, specify the Content property of the button as "Make a mosaic", and specify its Width property as "100". 連接Click事件button_Click加上的範例程式碼中定義的事件處理常式Click="button_Click"<Button>項目。Connect the Click event with the button_Click event handler defined in the example's code by adding Click="button_Click" to the <Button> element. 名稱的文字方塊屬性視窗中,命名控制 「 按鈕 」。In the Name textbox of the Properties window, name the control "button".

  3. MainWindow.xaml.cs 或 MainWindow.xaml.vb 檔案的整個內容取代這個範例的程式碼。Replace the entire contents of the MainWindow.xaml.cs or MainWindow.xaml.vb file with the code from this example. 針對C#WPF 專案中,請確定工作區的名稱符合專案名稱。For a C# WPF project, make sure that the name of the workspace matches the project name.

  4. 此範例會讀取名為雰枆 C:\Users\Public\Pictures\Sample Pictures 目錄中的 JPEG 影像\。The example reads JPEG images from a directory named C:\Users\Public\Pictures\Sample Pictures\. 建立目錄和放置一些映像,或變更以指向其他包含映像的目錄路徑。Either create the directory and place some images in it, or change the path to refer to some other directory that contains images.

此範例中會有一些限制。This example has some limitations. 例如,支援只有 32-位元-每個像素映像;其他格式的映像已損毀的BitmapImage調整大小作業期間的物件。For example, only 32-bits-per-pixel images are supported; images in other formats are corrupted by the BitmapImage object during the resizing operation. 此外,原始碼映像全都必須大於的並排顯示大小。Also, the source images must all be larger than the tile size. 為進一步的練習中,您可以新增功能,以處理多個像素格式和檔案大小。As a further exercise, you can add functionality to handle multiple pixel formats and file sizes.

建構函式

TaskScheduler() TaskScheduler() TaskScheduler() TaskScheduler()

初始化 TaskSchedulerInitializes the TaskScheduler.

屬性

Current Current Current Current

取得與目前執行之工作相關聯的 TaskSchedulerGets the TaskScheduler associated with the currently executing task.

Default Default Default Default

取得 .NET Framework 提供的預設 TaskScheduler 執行個體。Gets the default TaskScheduler instance that is provided by the .NET Framework.

Id Id Id Id

取得這個 TaskScheduler 的唯一 ID。Gets the unique ID for this TaskScheduler.

MaximumConcurrencyLevel MaximumConcurrencyLevel MaximumConcurrencyLevel MaximumConcurrencyLevel

表示這個 TaskScheduler 可以支援的最大並行層級。Indicates the maximum concurrency level this TaskScheduler is able to support.

方法

Equals(Object) Equals(Object) Equals(Object) Equals(Object)

判斷指定的物件是否等於目前的物件。Determines whether the specified object is equal to the current object.

(Inherited from Object)
Finalize() Finalize() Finalize() Finalize()

釋出所有與這個排程器相關聯的資源。Frees all resources associated with this scheduler.

FromCurrentSynchronizationContext() FromCurrentSynchronizationContext() FromCurrentSynchronizationContext() FromCurrentSynchronizationContext()

建立與目前 SynchronizationContext關聯的 TaskSchedulerCreates a TaskScheduler associated with the current SynchronizationContext.

GetHashCode() GetHashCode() GetHashCode() GetHashCode()

做為預設雜湊函式。Serves as the default hash function.

(Inherited from Object)
GetScheduledTasks() GetScheduledTasks() GetScheduledTasks() GetScheduledTasks()

僅限偵錯工具支援,針對目前已排入至排程器中等候執行的 Task 執行個體,產生可列舉項目。For debugger support only, generates an enumerable of Task instances currently queued to the scheduler waiting to be executed.

GetType() GetType() GetType() GetType()

取得目前執行個體的 TypeGets the Type of the current instance.

(Inherited from Object)
MemberwiseClone() MemberwiseClone() MemberwiseClone() MemberwiseClone()

建立目前 Object 的淺層複本 (Shallow Copy)。Creates a shallow copy of the current Object.

(Inherited from Object)
QueueTask(Task) QueueTask(Task) QueueTask(Task) QueueTask(Task)

Task 排入至排程器。Queues a Task to the scheduler.

ToString() ToString() ToString() ToString()

傳回代表目前物件的字串。Returns a string that represents the current object.

(Inherited from Object)
TryDequeue(Task) TryDequeue(Task) TryDequeue(Task) TryDequeue(Task)

嘗試清除之前排入至這個排程器的 TaskAttempts to dequeue a Task that was previously queued to this scheduler.

TryExecuteTask(Task) TryExecuteTask(Task) TryExecuteTask(Task) TryExecuteTask(Task)

嘗試在這個排程器上執行提供的 TaskAttempts to execute the provided Task on this scheduler.

TryExecuteTaskInline(Task, Boolean) TryExecuteTaskInline(Task, Boolean) TryExecuteTaskInline(Task, Boolean) TryExecuteTaskInline(Task, Boolean)

判斷是否可以在這個呼叫中同步執行所提供的 Task,如果可以,即加以執行。Determines whether the provided Task can be executed synchronously in this call, and if it can, executes it.

事件

UnobservedTaskException UnobservedTaskException UnobservedTaskException UnobservedTaskException

當錯誤工作未觀察到的例外狀況將觸發例外狀況擴大原則時發生,根據預設,這會終止處理程序。Occurs when a faulted task's unobserved exception is about to trigger exception escalation policy, which, by default, would terminate the process.

適用於

執行緒安全性

所有抽象成員TaskScheduler型別是安全執行緒,且可能會同時使用從多個執行緒。All members of the abstract TaskScheduler type are thread-safe and may be used from multiple threads concurrently.

另請參閱