Partilhar via


Como especificar um Agendador de Tarefas em um bloco de fluxo de dados

Este documento demonstra como associar um agendador de tarefas específico quando você usa o fluxo de dados em seu aplicativo. O exemplo usa a System.Threading.Tasks.ConcurrentExclusiveSchedulerPair classe em um aplicativo Windows Forms para mostrar quando as tarefas de leitura estão ativas e quando uma tarefa de gravador está ativa. Ele também usa o TaskScheduler.FromCurrentSynchronizationContext método para permitir que um bloco de fluxo de dados seja executado no thread da interface do usuário.

Nota

A biblioteca de fluxo de dados TPL (o namespace) não é distribuída com o System.Threading.Tasks.Dataflow .NET. Para instalar o System.Threading.Tasks.Dataflow namespace no Visual Studio, abra seu projeto, escolha Gerenciar pacotes NuGet no menu Projeto e pesquise o System.Threading.Tasks.Dataflow pacote online. Como alternativa, para instalá-lo usando a CLI do .NET Core, execute dotnet add package System.Threading.Tasks.Dataflow.

Para criar o aplicativo Windows Forms

  1. Crie um projeto Visual C# ou Visual Basic Windows Forms Application . Nas etapas a seguir, o projeto é chamado WriterReadersWinFormsde .

  2. No designer de formulário para o formulário principal, Form1.cs (Form1.vb para Visual Basic), adicione quatro CheckBox controles. Defina a Text propriedade como Reader 1 para checkBox1, Reader 2 para checkBox2, Reader 3 para checkBox3e Writer para checkBox4. Defina a Enabled propriedade de cada controle como False.

  3. Adicione um Timer controle ao formulário. Defina a propriedade Interval como 2500.

Adicionando funcionalidade de fluxo de dados

Esta seção descreve como criar os blocos de fluxo de dados que participam do aplicativo e como associar cada um a um agendador de tarefas.

Para adicionar funcionalidade de fluxo de dados ao aplicativo

  1. No seu projeto, adicione uma referência a System.Threading.Tasks.Dataflow.dll.

  2. Verifique se Form1.cs (Form1.vb para Visual Basic) contém as seguintes using diretivas (Imports no Visual Basic).

    using System;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;
    using System.Windows.Forms;
    
    Imports System.Threading
    Imports System.Threading.Tasks
    Imports System.Threading.Tasks.Dataflow
    
    
  3. Adicione um BroadcastBlock<T> membro de dados à Form1 classe.

    // Broadcasts values to an ActionBlock<int> object that is associated
    // with each check box.
    BroadcastBlock<int> broadcaster = new BroadcastBlock<int>(null);
    
    ' Broadcasts values to an ActionBlock<int> object that is associated
    ' with each check box.
    Private broadcaster As New BroadcastBlock(Of Integer)(Nothing)
    
  4. Form1 No construtor, após a chamada para InitializeComponent, crie um ActionBlock<TInput> objeto que alterna o estado dos CheckBox objetos.

    // Create an ActionBlock<CheckBox> object that toggles the state
    // of CheckBox objects.
    // Specifying the current synchronization context enables the
    // action to run on the user-interface thread.
    var toggleCheckBox = new ActionBlock<CheckBox>(checkBox =>
    {
       checkBox.Checked = !checkBox.Checked;
    },
    new ExecutionDataflowBlockOptions
    {
       TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
    });
    
    ' Create an ActionBlock<CheckBox> object that toggles the state
    ' of CheckBox objects.
    ' Specifying the current synchronization context enables the 
    ' action to run on the user-interface thread.
    Dim toggleCheckBox = New ActionBlock(Of CheckBox)(Sub(checkBox) checkBox.Checked = Not checkBox.Checked, New ExecutionDataflowBlockOptions With {.TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()})
    
  5. Form1 No construtor, crie um ConcurrentExclusiveSchedulerPair objeto e quatro ActionBlock<TInput> objetos, um ActionBlock<TInput> objeto para cada CheckBox objeto. Para cada ActionBlock<TInput> objeto, especifique um ExecutionDataflowBlockOptions objeto que tenha a TaskScheduler propriedade definida como a ConcurrentScheduler propriedade para os leitores e a ExclusiveScheduler propriedade para o gravador.

    // Create a ConcurrentExclusiveSchedulerPair object.
    // Readers will run on the concurrent part of the scheduler pair.
    // The writer will run on the exclusive part of the scheduler pair.
    var taskSchedulerPair = new ConcurrentExclusiveSchedulerPair();
    
    // Create an ActionBlock<int> object for each reader CheckBox object.
    // Each ActionBlock<int> object represents an action that can read
    // from a resource in parallel to other readers.
    // Specifying the concurrent part of the scheduler pair enables the
    // reader to run in parallel to other actions that are managed by
    // that scheduler.
    var readerActions =
       from checkBox in new CheckBox[] {checkBox1, checkBox2, checkBox3}
       select new ActionBlock<int>(milliseconds =>
       {
          // Toggle the check box to the checked state.
          toggleCheckBox.Post(checkBox);
    
          // Perform the read action. For demonstration, suspend the current
          // thread to simulate a lengthy read operation.
          Thread.Sleep(milliseconds);
    
          // Toggle the check box to the unchecked state.
          toggleCheckBox.Post(checkBox);
       },
       new ExecutionDataflowBlockOptions
       {
          TaskScheduler = taskSchedulerPair.ConcurrentScheduler
       });
    
    // Create an ActionBlock<int> object for the writer CheckBox object.
    // This ActionBlock<int> object represents an action that writes to
    // a resource, but cannot run in parallel to readers.
    // Specifying the exclusive part of the scheduler pair enables the
    // writer to run in exclusively with respect to other actions that are
    // managed by the scheduler pair.
    var writerAction = new ActionBlock<int>(milliseconds =>
    {
       // Toggle the check box to the checked state.
       toggleCheckBox.Post(checkBox4);
    
       // Perform the write action. For demonstration, suspend the current
       // thread to simulate a lengthy write operation.
       Thread.Sleep(milliseconds);
    
       // Toggle the check box to the unchecked state.
       toggleCheckBox.Post(checkBox4);
    },
    new ExecutionDataflowBlockOptions
    {
       TaskScheduler = taskSchedulerPair.ExclusiveScheduler
    });
    
    // Link the broadcaster to each reader and writer block.
    // The BroadcastBlock<T> class propagates values that it
    // receives to all connected targets.
    foreach (var readerAction in readerActions)
    {
       broadcaster.LinkTo(readerAction);
    }
    broadcaster.LinkTo(writerAction);
    
    ' Create a ConcurrentExclusiveSchedulerPair object.
    ' Readers will run on the concurrent part of the scheduler pair.
    ' The writer will run on the exclusive part of the scheduler pair.
    Dim taskSchedulerPair = New ConcurrentExclusiveSchedulerPair()
    
    ' Create an ActionBlock<int> object for each reader CheckBox object.
    ' Each ActionBlock<int> object represents an action that can read 
    ' from a resource in parallel to other readers.
    ' Specifying the concurrent part of the scheduler pair enables the 
    ' reader to run in parallel to other actions that are managed by 
    ' that scheduler.
    Dim readerActions = From checkBox In New CheckBox() {checkBox1, checkBox2, checkBox3} _
                        Select New ActionBlock(Of Integer)(Sub(milliseconds)
                                               ' Toggle the check box to the checked state.
                                               ' Perform the read action. For demonstration, suspend the current
                                               ' thread to simulate a lengthy read operation.
                                               ' Toggle the check box to the unchecked state.
                                               toggleCheckBox.Post(checkBox)
                                                               Thread.Sleep(milliseconds)
                                                               toggleCheckBox.Post(checkBox)
                                                           End Sub, New ExecutionDataflowBlockOptions With {.TaskScheduler = taskSchedulerPair.ConcurrentScheduler})
    
    ' Create an ActionBlock<int> object for the writer CheckBox object.
    ' This ActionBlock<int> object represents an action that writes to 
    ' a resource, but cannot run in parallel to readers.
    ' Specifying the exclusive part of the scheduler pair enables the 
    ' writer to run in exclusively with respect to other actions that are 
    ' managed by the scheduler pair.
    Dim writerAction = New ActionBlock(Of Integer)(Sub(milliseconds)
                                                       ' Toggle the check box to the checked state.
                                                       ' Perform the write action. For demonstration, suspend the current
                                                       ' thread to simulate a lengthy write operation.
                                                       ' Toggle the check box to the unchecked state.
                                                       toggleCheckBox.Post(checkBox4)
                                                       Thread.Sleep(milliseconds)
                                                       toggleCheckBox.Post(checkBox4)
                                                   End Sub, New ExecutionDataflowBlockOptions With {.TaskScheduler = taskSchedulerPair.ExclusiveScheduler})
    
    ' Link the broadcaster to each reader and writer block.
    ' The BroadcastBlock<T> class propagates values that it 
    ' receives to all connected targets.
    For Each readerAction In readerActions
        broadcaster.LinkTo(readerAction)
    Next readerAction
    broadcaster.LinkTo(writerAction)
    
  6. Form1 No construtor, inicie o Timer objeto.

    // Start the timer.
    timer1.Start();
    
    ' Start the timer.
    timer1.Start()
    
  7. No designer de formulário para o formulário principal, crie um manipulador de eventos para o Tick evento do temporizador.

  8. Implemente o Tick evento para o temporizador.

    // Event handler for the timer.
    private void timer1_Tick(object sender, EventArgs e)
    {
       // Post a value to the broadcaster. The broadcaster
       // sends this message to each target.
       broadcaster.Post(1000);
    }
    
    ' Event handler for the timer.
    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles timer1.Tick
        ' Post a value to the broadcaster. The broadcaster
        ' sends this message to each target. 
        broadcaster.Post(1000)
    End Sub
    

Como o toggleCheckBox bloco de fluxo de dados atua na interface do usuário, é importante que essa ação ocorra no thread da interface do usuário. Para fazer isso, durante a construção, esse objeto fornece um ExecutionDataflowBlockOptions objeto que tem a TaskScheduler propriedade definida como TaskScheduler.FromCurrentSynchronizationContext. O FromCurrentSynchronizationContext método cria um objeto que executa trabalho TaskScheduler no contexto de sincronização atual. Como o Form1 construtor é chamado a partir do thread da interface do usuário, a ação para o toggleCheckBox bloco de fluxo de dados também é executada no thread da interface do usuário.

Este exemplo também usa a ConcurrentExclusiveSchedulerPair classe para permitir que alguns blocos de fluxo de dados ajam simultaneamente e outro bloco de fluxo de dados aja exclusivamente em relação a todos os outros blocos de fluxo de dados executados no mesmo ConcurrentExclusiveSchedulerPair objeto. Essa técnica é útil quando vários blocos de fluxo de dados compartilham um recurso e alguns exigem acesso exclusivo a esse recurso, porque elimina a necessidade de sincronizar manualmente o acesso a esse recurso. A eliminação da sincronização manual pode tornar o código mais eficiente.

Exemplo

O exemplo a seguir mostra o código completo para Form1.cs (Form1.vb para Visual Basic).

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;

namespace WriterReadersWinForms
{
   public partial class Form1 : Form
   {
      // Broadcasts values to an ActionBlock<int> object that is associated
      // with each check box.
      BroadcastBlock<int> broadcaster = new BroadcastBlock<int>(null);

      public Form1()
      {
         InitializeComponent();

         // Create an ActionBlock<CheckBox> object that toggles the state
         // of CheckBox objects.
         // Specifying the current synchronization context enables the
         // action to run on the user-interface thread.
         var toggleCheckBox = new ActionBlock<CheckBox>(checkBox =>
         {
            checkBox.Checked = !checkBox.Checked;
         },
         new ExecutionDataflowBlockOptions
         {
            TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
         });

         // Create a ConcurrentExclusiveSchedulerPair object.
         // Readers will run on the concurrent part of the scheduler pair.
         // The writer will run on the exclusive part of the scheduler pair.
         var taskSchedulerPair = new ConcurrentExclusiveSchedulerPair();

         // Create an ActionBlock<int> object for each reader CheckBox object.
         // Each ActionBlock<int> object represents an action that can read
         // from a resource in parallel to other readers.
         // Specifying the concurrent part of the scheduler pair enables the
         // reader to run in parallel to other actions that are managed by
         // that scheduler.
         var readerActions =
            from checkBox in new CheckBox[] {checkBox1, checkBox2, checkBox3}
            select new ActionBlock<int>(milliseconds =>
            {
               // Toggle the check box to the checked state.
               toggleCheckBox.Post(checkBox);

               // Perform the read action. For demonstration, suspend the current
               // thread to simulate a lengthy read operation.
               Thread.Sleep(milliseconds);

               // Toggle the check box to the unchecked state.
               toggleCheckBox.Post(checkBox);
            },
            new ExecutionDataflowBlockOptions
            {
               TaskScheduler = taskSchedulerPair.ConcurrentScheduler
            });

         // Create an ActionBlock<int> object for the writer CheckBox object.
         // This ActionBlock<int> object represents an action that writes to
         // a resource, but cannot run in parallel to readers.
         // Specifying the exclusive part of the scheduler pair enables the
         // writer to run in exclusively with respect to other actions that are
         // managed by the scheduler pair.
         var writerAction = new ActionBlock<int>(milliseconds =>
         {
            // Toggle the check box to the checked state.
            toggleCheckBox.Post(checkBox4);

            // Perform the write action. For demonstration, suspend the current
            // thread to simulate a lengthy write operation.
            Thread.Sleep(milliseconds);

            // Toggle the check box to the unchecked state.
            toggleCheckBox.Post(checkBox4);
         },
         new ExecutionDataflowBlockOptions
         {
            TaskScheduler = taskSchedulerPair.ExclusiveScheduler
         });

         // Link the broadcaster to each reader and writer block.
         // The BroadcastBlock<T> class propagates values that it
         // receives to all connected targets.
         foreach (var readerAction in readerActions)
         {
            broadcaster.LinkTo(readerAction);
         }
         broadcaster.LinkTo(writerAction);

         // Start the timer.
         timer1.Start();
      }

      // Event handler for the timer.
      private void timer1_Tick(object sender, EventArgs e)
      {
         // Post a value to the broadcaster. The broadcaster
         // sends this message to each target.
         broadcaster.Post(1000);
      }
   }
}
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Threading.Tasks.Dataflow


Namespace WriterReadersWinForms
    Partial Public Class Form1
        Inherits Form
        ' Broadcasts values to an ActionBlock<int> object that is associated
        ' with each check box.
        Private broadcaster As New BroadcastBlock(Of Integer)(Nothing)

        Public Sub New()
            InitializeComponent()

            ' Create an ActionBlock<CheckBox> object that toggles the state
            ' of CheckBox objects.
            ' Specifying the current synchronization context enables the 
            ' action to run on the user-interface thread.
            Dim toggleCheckBox = New ActionBlock(Of CheckBox)(Sub(checkBox) checkBox.Checked = Not checkBox.Checked, New ExecutionDataflowBlockOptions With {.TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()})

            ' Create a ConcurrentExclusiveSchedulerPair object.
            ' Readers will run on the concurrent part of the scheduler pair.
            ' The writer will run on the exclusive part of the scheduler pair.
            Dim taskSchedulerPair = New ConcurrentExclusiveSchedulerPair()

            ' Create an ActionBlock<int> object for each reader CheckBox object.
            ' Each ActionBlock<int> object represents an action that can read 
            ' from a resource in parallel to other readers.
            ' Specifying the concurrent part of the scheduler pair enables the 
            ' reader to run in parallel to other actions that are managed by 
            ' that scheduler.
            Dim readerActions = From checkBox In New CheckBox() {checkBox1, checkBox2, checkBox3} _
                                Select New ActionBlock(Of Integer)(Sub(milliseconds)
                                                       ' Toggle the check box to the checked state.
                                                       ' Perform the read action. For demonstration, suspend the current
                                                       ' thread to simulate a lengthy read operation.
                                                       ' Toggle the check box to the unchecked state.
                                                       toggleCheckBox.Post(checkBox)
                                                                       Thread.Sleep(milliseconds)
                                                                       toggleCheckBox.Post(checkBox)
                                                                   End Sub, New ExecutionDataflowBlockOptions With {.TaskScheduler = taskSchedulerPair.ConcurrentScheduler})

            ' Create an ActionBlock<int> object for the writer CheckBox object.
            ' This ActionBlock<int> object represents an action that writes to 
            ' a resource, but cannot run in parallel to readers.
            ' Specifying the exclusive part of the scheduler pair enables the 
            ' writer to run in exclusively with respect to other actions that are 
            ' managed by the scheduler pair.
            Dim writerAction = New ActionBlock(Of Integer)(Sub(milliseconds)
                                                               ' Toggle the check box to the checked state.
                                                               ' Perform the write action. For demonstration, suspend the current
                                                               ' thread to simulate a lengthy write operation.
                                                               ' Toggle the check box to the unchecked state.
                                                               toggleCheckBox.Post(checkBox4)
                                                               Thread.Sleep(milliseconds)
                                                               toggleCheckBox.Post(checkBox4)
                                                           End Sub, New ExecutionDataflowBlockOptions With {.TaskScheduler = taskSchedulerPair.ExclusiveScheduler})

            ' Link the broadcaster to each reader and writer block.
            ' The BroadcastBlock<T> class propagates values that it 
            ' receives to all connected targets.
            For Each readerAction In readerActions
                broadcaster.LinkTo(readerAction)
            Next readerAction
            broadcaster.LinkTo(writerAction)

            ' Start the timer.
            timer1.Start()
        End Sub

        ' Event handler for the timer.
        Private Sub timer1_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles timer1.Tick
            ' Post a value to the broadcaster. The broadcaster
            ' sends this message to each target. 
            broadcaster.Post(1000)
        End Sub
    End Class
End Namespace

Consulte também