TaskScheduler Classe

Definição

Representa um objeto que trata do trabalho de baixo nível de enfileirar tarefas em threads.

public ref class TaskScheduler abstract
public abstract class TaskScheduler
type TaskScheduler = class
Public MustInherit Class TaskScheduler
Herança
TaskScheduler

Exemplos

O exemplo seguinte cria um agendador de tarefas personalizado que limita o número de threads usados pela aplicação. De seguida, lança dois conjuntos de tarefas e apresenta informações sobre a tarefa e o thread em que a tarefa está a ser executada.

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

Observações

A TaskScheduler classe representa um agendador de tarefas. Um agendador de tarefas garante que o trabalho de uma tarefa seja eventualmente executado.

O agendador de tarefas padrão fornece roubo de tarefas para balanceamento de carga, injeção e retirada de threads para máximo rendimento e bom desempenho geral. Deve ser suficiente para a maioria dos cenários.

A TaskScheduler classe também serve como o ponto de extensão para toda a lógica de agendamento personalizável. Isso inclui mecanismos como como agendar uma tarefa para execução e como as tarefas agendadas devem ser expostas aos depuradores. Se você precisar de funcionalidade especial, poderá criar um agendador personalizado e habilitá-lo para tarefas ou consultas específicas.

O agendador de tarefas padrão e o pool de threads

O agendador padrão para a Biblioteca Paralela de Tarefas e PLINQ usa o pool de threads .NET, que é representado pela ThreadPool classe, para enfileirar e executar trabalho. O pool de threads usa as informações fornecidas pelo Task tipo para oferecer suporte eficiente ao paralelismo refinado (unidades de trabalho de curta duração) que tarefas paralelas e consultas geralmente representam.

A fila global versus filas locais

O pool de threads mantém uma fila de trabalho FIFO global (first-in, first-out) para threads em cada domínio de aplicativo. Sempre que um programa chama o ThreadPool.QueueUserWorkItem método (ou ThreadPool.UnsafeQueueUserWorkItem), o trabalho é colocado nessa fila compartilhada e, eventualmente, retirado da fila para o próximo thread que fica disponível. A partir do .NET Framework 4, essa fila usa um algoritmo sem bloqueio semelhante à ConcurrentQueue<T> classe. Usando essa implementação sem bloqueio, o pool de threads gasta menos tempo quando enfileira e elimina a fila de itens de trabalho. Esse benefício de desempenho está disponível para todos os programas que usam o pool de threads.

As tarefas de nível superior, que são tarefas que não são criadas no contexto de outra tarefa, são colocadas na fila global como qualquer outro item de trabalho. No entanto, as tarefas aninhadas ou subordinadas, que são criadas no contexto de outra tarefa, são tratadas de forma bastante diferente. Uma tarefa filha ou aninhada é colocada numa fila local específica para a thread onde a tarefa principal está a ser executada. A tarefa pai pode ser uma tarefa de nível superior ou também pode ser filha de outra tarefa. Quando este thread estiver pronto para receber mais trabalho, primeiro irá procurar na fila local. Se os itens de trabalho estiverem esperando lá, eles podem ser acessados rapidamente. As filas locais são acessadas na última ordem de entrada e primeira saída (LIFO) para preservar a localidade do cache e reduzir a contenção. Para obter mais informações sobre tarefas descendentes e tarefas aninhadas, consulte Tarefas descendentes e aninhadas anexadas e desanexadas.

O uso de filas locais não só reduz a pressão sobre a fila global, mas também aproveita a localidade de dados. Os itens de trabalho na fila local frequentemente fazem referência a estruturas de dados que estão fisicamente próximas umas das outras na memória. Nesses casos, os dados já estão no cache após a execução da primeira tarefa e podem ser acessados rapidamente. Tanto o LINQ paralelo (PLINQ) quanto a Parallel classe usam amplamente tarefas aninhadas e tarefas filhas e conseguem ganhos de velocidade significativos usando filas de trabalho locais.

Roubo de trabalho

A partir do .NET Framework 4, o pool de threads também implementa um algoritmo de desvio de tarefas para ajudar a garantir que nenhuma thread fique ociosa enquanto outras ainda têm tarefas nas suas filas. Quando um thread de pool está pronto para mais trabalho, ele primeiro olha para a cabeça da sua fila local, depois para a fila global e, em seguida, para as filas locais de outros threads. Se ele encontrar um item de trabalho na fila local de outro thread, ele primeiro aplicará heurísticas para garantir que ele possa executar o trabalho de forma eficiente. Se puder, ele tira o item de trabalho do fim da fila (na ordem FIFO). Isso reduz a contenção em cada fila local e preserva a localidade dos dados. Essa arquitetura ajuda o trabalho de balanceamento de carga do pool de threads de forma mais eficiente do que as versões anteriores.

Tarefas de longa duração

Talvez você queira impedir explicitamente que uma tarefa seja colocada em uma fila local. Por exemplo, você pode saber que um determinado item de trabalho será executado por um tempo relativamente longo e provavelmente bloqueará todos os outros itens de trabalho na fila local. Nesse caso, você pode especificar a System.Threading.Tasks.TaskCreationOptions opção, que fornece uma dica ao agendador de que um thread adicional pode ser necessário para a tarefa, para que ele não bloqueie o progresso de outros threads ou itens de trabalho na fila local. Ao usar essa opção, você evita completamente o pool de threads, incluindo as filas globais e locais.

Integração de tarefas

Em alguns casos, quando um Task é aguardado, ele pode ser executado de forma síncrona no thread que está executando a operação de espera. Isso melhora o desempenho, evitando a necessidade de um thread adicional e, em vez disso, usando o thread existente, que teria bloqueado de outra forma. Para evitar erros devido à reentrância, a inserção de tarefas só ocorre quando o destino de espera é encontrado na fila local do thread relevante.

Especificar um contexto de sincronização

Você pode usar o TaskScheduler.FromCurrentSynchronizationContext método para especificar que uma tarefa deve ser agendada para ser executada em um thread específico. Isso é útil em estruturas como Windows Forms e Windows Presentation Foundation, onde o acesso a objetos da interface do usuário geralmente é restrito ao código que está sendo executado no mesmo thread no qual o objeto da interface do usuário foi criado.

O exemplo a seguir usa o TaskScheduler.FromCurrentSynchronizationContext método em um aplicativo Windows Presentation Foundation (WPF) para agendar uma tarefa no mesmo thread em que o controle de interface do usuário (UI) foi criado. O exemplo cria um mosaico de imagens que são selecionadas aleatoriamente de um diretório especificado. Os objetos WPF são usados para carregar e redimensionar as imagens. Os pixels brutos são então passados para uma tarefa que usa um For loop para gravar os dados de pixel em uma grande matriz de byte único. Nenhuma sincronização é necessária porque não há dois blocos ocupando os mesmos elementos de matriz. Os blocos também podem ser escritos em qualquer ordem, porque sua posição é calculada independentemente de qualquer outro bloco. A matriz grande é então passada para uma tarefa que é executada no thread da interface do usuário, onde os dados de pixel são carregados em um controle Image.

O exemplo move dados do thread da interface de utilizador, modifica-os usando loops paralelos e objectos Task, e depois passa-os de volta para uma tarefa que é executada no thread da interface de utilizador. Essa abordagem é útil quando você precisa usar a Biblioteca Paralela de Tarefas para executar operações que não são suportadas pela API do WPF ou não são suficientemente rápidas. Outra maneira de criar um mosaico de imagens no WPF é usar um System.Windows.Controls.WrapPanel controle e adicionar imagens a ele. A WrapPanel executa o posicionamento dos azulejos. No entanto, esse trabalho só pode ser executado no thread da interface do usuário.

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;
        }
    }
}

Para criar o exemplo, crie um projeto de aplicativo WPF no Visual Studio e nomeie-o WPF_CS1 (para um projeto WPF em C#) ou WPF_VB1 (para um projeto WPF do Visual Basic). Em seguida, faça o seguinte:

  1. No modo de exibição de design, arraste um Image controle da Caixa de Ferramentas para o canto superior esquerdo da superfície de design. Na caixa de texto Nome da janela Propriedades , nomeie o controle como "imagem".

  2. Arraste um Button controle da Caixa de Ferramentas para a parte inferior esquerda da janela do aplicativo. No modo de exibição XAML, especifique a propriedade do botão como "Criar um mosaico Content " e especifique sua Width propriedade como "100". Conecte o Click evento com o button_Click manipulador de eventos definido no código do exemplo adicionando Click="button_Click" ao <Button> elemento . Na caixa de texto Nome da janela Propriedades , nomeie o controle como "botão".

  3. Substitua todo o conteúdo do arquivo MainWindow.xaml.cs ou MainWindow.xaml.vb pelo código deste exemplo. Para um projeto WPF C#, certifique-se de que o nome do espaço de trabalho corresponde ao nome do projeto.

  4. O exemplo lê imagens JPEG de um diretório chamado C:\Users\Public\Pictures\Sample Pictures. Crie o diretório e coloque algumas imagens nele ou altere o caminho para fazer referência a algum outro diretório que contenha imagens.

Este exemplo tem algumas limitações. Por exemplo, apenas imagens de 32 bits por pixel são suportadas; imagens em outros formatos são corrompidas pelo objeto BitmapImage durante a operação de redimensionamento. Além disso, todas as imagens de origem devem ser maiores do que o tamanho do bloco. Como exercício adicional, você pode adicionar funcionalidade para lidar com vários formatos de pixel e tamanhos de arquivo.

Construtores

Name Descrição
TaskScheduler()

Inicializa o TaskScheduler.

Propriedades

Name Descrição
Current

Obtém a TaskScheduler tarefa associada à tarefa que está a ser executada.

Default

Recebe a instância padrão TaskScheduler fornecida pela .NET.

Id

Obtém o ID único para isto TaskScheduler.

MaximumConcurrencyLevel

Indica o nível máximo de concorrência que este TaskScheduler consegue suportar.

Métodos

Name Descrição
Equals(Object)

Determina se o objeto especificado é igual ao objeto atual.

(Herdado de Object)
Finalize()

Liberta todos os recursos associados a este agendador.

FromCurrentSynchronizationContext()

Cria um TaskScheduler associado à corrente SynchronizationContext.

GetHashCode()

Serve como função de hash predefinida.

(Herdado de Object)
GetScheduledTasks()

Para suporte apenas de depuradores, gera um número incontável de Task instâncias atualmente enfileiradas no escalonador à espera de serem executadas.

GetType()

Obtém o Type da instância atual.

(Herdado de Object)
MemberwiseClone()

Cria uma cópia superficial do atual Object.

(Herdado de Object)
QueueTask(Task)

Enfila A Task para o agendador.

ToString()

Devolve uma cadeia que representa o objeto atual.

(Herdado de Object)
TryDequeue(Task)

Tentativas de desalinhar um Task que anteriormente estava em fila para este agendador.

TryExecuteTask(Task)

Tenta executar o fornecido Task neste agendador.

TryExecuteTaskInline(Task, Boolean)

Determina se o fornecido Task pode ser executado de forma síncrona nesta chamada e, se possível, executa-o.

Eventos

Name Descrição
UnobservedTaskException

Ocorre quando a exceção não observada de uma tarefa com falha está prestes a desencadear a política de escalonamento de exceções, que, por defeito, terminaria o processo.

Aplica-se a

Segurança de Thread

Todos os membros do tipo abstrato TaskScheduler são seguros para threads e podem ser usados a partir de múltiplas threads em simultâneo.

Ver também