Share via


Procedura: annullare un blocco di flussi di dati

In questo documento viene dimostrato come abilitare l'annullamento nell'applicazione. In questo esempio Windows Form viene usato per mostrare dove gli elementi di lavoro sono attivi in una pipeline del flusso di dati, nonché gli effetti dell'annullamento.

Nota

La libreria del flusso di dati TPL (spazio dei nomi System.Threading.Tasks.Dataflow) non viene distribuita con .NET. Per installare lo spazio dei nomi System.Threading.Tasks.Dataflow in Visual Studio, aprire il progetto in Visual Studio, scegliere Gestisci pacchetti NuGet dal menu Progetto ed eseguire una ricerca online del pacchetto System.Threading.Tasks.Dataflow. In alternativa, per installarlo usando l'interfaccia della riga di comando di .NET Core, eseguire dotnet add package System.Threading.Tasks.Dataflow.

Per creare l'applicazione Windows Forms

  1. Creare un progetto Windows Forms Application di C# o Visual Basic. Nei passaggi seguenti il progetto viene denominato CancellationWinForms.

  2. Nella finestra di progettazione del form per il form principale, Form1.cs (Form1.vb per Visual Basic), aggiungere un controllo ToolStrip.

  3. Aggiungere un controllo ToolStripButton al controllo ToolStrip. Impostare la proprietà DisplayStyle su Text e la proprietà Text su Aggiungi elementi di lavoro.

  4. Aggiungere un secondo controllo ToolStripButton al controllo ToolStrip. Impostare la proprietà DisplayStyle su Text, la proprietà Text su Annulla e la proprietà Enabled su False.

  5. Aggiungere quattro oggetti ToolStripProgressBar al controllo ToolStrip.

Creazione della pipeline del flusso di dati

In questa sezione viene descritto come creare la pipeline del flusso di dati che elabora gli elementi di lavoro e aggiorna le barre di stato.

Per creare la pipeline del flusso di dati

  1. Nel progetto aggiungere un riferimento a System.Threading.Tasks.Dataflow.dll.

  2. Assicurarsi che in Form1.cs (Form1.vb per Visual Basic) siano contenute le seguenti istruzioni using (Imports in Visual Basic).

    using System;
    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. Aggiungere la classe WorkItem come un tipo interno della classe Form1.

    // A placeholder type that performs work.
    class WorkItem
    {
       // Performs work for the provided number of milliseconds.
       public void DoWork(int milliseconds)
       {
          // For demonstration, suspend the current thread.
          Thread.Sleep(milliseconds);
       }
    }
    
    ' A placeholder type that performs work.
    Private Class WorkItem
        ' Performs work for the provided number of milliseconds.
        Public Sub DoWork(ByVal milliseconds As Integer)
            ' For demonstration, suspend the current thread.
            Thread.Sleep(milliseconds)
        End Sub
    End Class
    
  4. Aggiungere i membri dei dati seguenti alla classe Form1.

    // Enables the user interface to signal cancellation.
    CancellationTokenSource cancellationSource;
    
    // The first node in the dataflow pipeline.
    TransformBlock<WorkItem, WorkItem> startWork;
    
    // The second, and final, node in the dataflow pipeline.
    ActionBlock<WorkItem> completeWork;
    
    // Increments the value of the provided progress bar.
    ActionBlock<ToolStripProgressBar> incrementProgress;
    
    // Decrements the value of the provided progress bar.
    ActionBlock<ToolStripProgressBar> decrementProgress;
    
    // Enables progress bar actions to run on the UI thread.
    TaskScheduler uiTaskScheduler;
    
    ' Enables the user interface to signal cancellation.
    Private cancellationSource As CancellationTokenSource
    
    ' The first node in the dataflow pipeline.
    Private startWork As TransformBlock(Of WorkItem, WorkItem)
    
    ' The second, and final, node in the dataflow pipeline.
    Private completeWork As ActionBlock(Of WorkItem)
    
    ' Increments the value of the provided progress bar.
    Private incrementProgress As ActionBlock(Of ToolStripProgressBar)
    
    ' Decrements the value of the provided progress bar.
    Private decrementProgress As ActionBlock(Of ToolStripProgressBar)
    
    ' Enables progress bar actions to run on the UI thread.
    Private uiTaskScheduler As TaskScheduler
    
  5. Aggiungere il metodo CreatePipeline seguente alla classe Form1.

    // Creates the blocks that participate in the dataflow pipeline.
    private void CreatePipeline()
    {
       // Create the cancellation source.
       cancellationSource = new CancellationTokenSource();
    
       // Create the first node in the pipeline.
       startWork = new TransformBlock<WorkItem, WorkItem>(workItem =>
       {
          // Perform some work.
          workItem.DoWork(250);
    
          // Decrement the progress bar that tracks the count of
          // active work items in this stage of the pipeline.
          decrementProgress.Post(toolStripProgressBar1);
    
          // Increment the progress bar that tracks the count of
          // active work items in the next stage of the pipeline.
          incrementProgress.Post(toolStripProgressBar2);
    
          // Send the work item to the next stage of the pipeline.
          return workItem;
       },
       new ExecutionDataflowBlockOptions
       {
          CancellationToken = cancellationSource.Token
       });
    
       // Create the second, and final, node in the pipeline.
       completeWork = new ActionBlock<WorkItem>(workItem =>
       {
          // Perform some work.
          workItem.DoWork(1000);
    
          // Decrement the progress bar that tracks the count of
          // active work items in this stage of the pipeline.
          decrementProgress.Post(toolStripProgressBar2);
    
          // Increment the progress bar that tracks the overall
          // count of completed work items.
          incrementProgress.Post(toolStripProgressBar3);
       },
       new ExecutionDataflowBlockOptions
       {
          CancellationToken = cancellationSource.Token,
          MaxDegreeOfParallelism = 2
       });
    
       // Connect the two nodes of the pipeline. When the first node completes,
       // set the second node also to the completed state.
       startWork.LinkTo(
          completeWork, new DataflowLinkOptions { PropagateCompletion = true });
    
       // Create the dataflow action blocks that increment and decrement
       // progress bars.
       // These blocks use the task scheduler that is associated with
       // the UI thread.
    
       incrementProgress = new ActionBlock<ToolStripProgressBar>(
          progressBar => progressBar.Value++,
          new ExecutionDataflowBlockOptions
          {
             CancellationToken = cancellationSource.Token,
             TaskScheduler = uiTaskScheduler
          });
    
       decrementProgress = new ActionBlock<ToolStripProgressBar>(
          progressBar => progressBar.Value--,
          new ExecutionDataflowBlockOptions
          {
             CancellationToken = cancellationSource.Token,
             TaskScheduler = uiTaskScheduler
          });
    }
    
    ' Creates the blocks that participate in the dataflow pipeline.
    Private Sub CreatePipeline()
        ' Create the cancellation source.
        cancellationSource = New CancellationTokenSource()
    
        ' Create the first node in the pipeline.
        startWork = New TransformBlock(Of WorkItem, WorkItem)(Function(workItem)
                                                                  ' Perform some work.
                                                                  ' Decrement the progress bar that tracks the count of
                                                                  ' active work items in this stage of the pipeline.
                                                                  ' Increment the progress bar that tracks the count of
                                                                  ' active work items in the next stage of the pipeline.
                                                                  ' Send the work item to the next stage of the pipeline.
                                                                  workItem.DoWork(250)
                                                                  decrementProgress.Post(toolStripProgressBar1)
                                                                  incrementProgress.Post(toolStripProgressBar2)
                                                                  Return workItem
                                                              End Function,
        New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})
    
        ' Create the second, and final, node in the pipeline.
        completeWork = New ActionBlock(Of WorkItem)(Sub(workItem)
                                                        ' Perform some work.
                                                        ' Decrement the progress bar that tracks the count of
                                                        ' active work items in this stage of the pipeline.
                                                        ' Increment the progress bar that tracks the overall
                                                        ' count of completed work items.
                                                        workItem.DoWork(1000)
                                                        decrementProgress.Post(toolStripProgressBar2)
                                                        incrementProgress.Post(toolStripProgressBar3)
                                                    End Sub,
        New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                .MaxDegreeOfParallelism = 2})
    
        ' Connect the two nodes of the pipeline. When the first node completes,
        ' set the second node also to the completed state.
        startWork.LinkTo(
           completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})
    
        ' Create the dataflow action blocks that increment and decrement
        ' progress bars.
        ' These blocks use the task scheduler that is associated with
        ' the UI thread.
    
        incrementProgress = New ActionBlock(Of ToolStripProgressBar)(
           Sub(progressBar) progressBar.Value += 1,
           New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                   .TaskScheduler = uiTaskScheduler})
    
        decrementProgress = New ActionBlock(Of ToolStripProgressBar)(
           Sub(progressBar) progressBar.Value -= 1,
           New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                   .TaskScheduler = uiTaskScheduler})
    
    End Sub
    

Poiché i blocchi di flussi di dati incrementProgress e decrementProgress vengono usati nell'interfaccia utente, è importante che queste azioni si verifichino nel thread di interfaccia utente. A questo scopo, durante la costruzione ognuno di questi oggetti fornisce un oggetto ExecutionDataflowBlockOptions la cui proprietà TaskScheduler è impostata su TaskScheduler.FromCurrentSynchronizationContext. Tramite il metodo TaskScheduler.FromCurrentSynchronizationContext viene creato un oggetto TaskScheduler mediante il quale viene eseguito il lavoro nel contesto di sincronizzazione corrente. Poiché il costruttore Form1 viene chiamato dal thread dell'interfaccia utente, le azioni per i blocchi di flussi di dati incrementProgress e decrementProgress vengono eseguite anche nel thread dell'interfaccia utente.

Questo esempio imposta la proprietà CancellationToken durante la costruzione dei membri della pipeline. Poiché la proprietà CancellationToken annulla definitivamente l'esecuzione del blocco del flusso di dati, l'intera pipeline deve essere ricreata dopo che l'utente annulla l'operazione e quindi vuole aggiungere più elementi di lavoro alla pipeline. Per un esempio che mostra un modo alternativo per annullare un blocco del flusso di dati per poter eseguire un altro lavoro dopo che viene annullata un'operazione, vedere Walkthrough: Using Dataflow in a Windows Forms Application (Procedura dettagliata: Uso del flusso di dati in un'applicazione Windows Forms).

Connessione della pipeline del flusso di dati all'interfaccia utente

In questa sezione viene descritto come connettere la pipeline del flusso di dati all'interfaccia utente. Sia la creazione della pipeline sia l'aggiunta di elementi di lavoro alla pipeline sono controllate dal gestore eventi tramite il pulsante Aggiungi elementi di lavoro. L'annullamento viene avviato dal pulsante Annulla. Quando l'utente fa clic su uno di questi pulsanti, l'azione appropriata viene avviata in modo asincrono.

Per connettere la pipeline del flusso di dati all'interfaccia utente

  1. Nella finestra di progettazione del form principale creare un gestore eventi per l'evento Click per il pulsante Aggiungi elementi di lavoro.

  2. Implementare l'evento Click per il pulsante Aggiungi elementi di lavoro.

    // Event handler for the Add Work Items button.
    private void toolStripButton1_Click(object sender, EventArgs e)
    {
       // The Cancel button is disabled when the pipeline is not active.
       // Therefore, create the pipeline and enable the Cancel button
       // if the Cancel button is disabled.
       if (!toolStripButton2.Enabled)
       {
          CreatePipeline();
    
          // Enable the Cancel button.
          toolStripButton2.Enabled = true;
       }
    
       // Post several work items to the head of the pipeline.
       for (int i = 0; i < 5; i++)
       {
          toolStripProgressBar1.Value++;
          startWork.Post(new WorkItem());
       }
    }
    
    ' Event handler for the Add Work Items button.
    Private Sub toolStripButton1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton1.Click
        ' The Cancel button is disabled when the pipeline is not active.
        ' Therefore, create the pipeline and enable the Cancel button
        ' if the Cancel button is disabled.
        If Not toolStripButton2.Enabled Then
            CreatePipeline()
    
            ' Enable the Cancel button.
            toolStripButton2.Enabled = True
        End If
    
        ' Post several work items to the head of the pipeline.
        For i As Integer = 0 To 4
            toolStripProgressBar1.Value += 1
            startWork.Post(New WorkItem())
        Next i
    End Sub
    
  3. Nella finestra di progettazione del form principale creare un gestore eventi per il gestore eventi Click per il pulsante Annulla.

  4. Implementare l'evento Click per il pulsante Annulla.

    // Event handler for the Cancel button.
    private async void toolStripButton2_Click(object sender, EventArgs e)
    {
       // Disable both buttons.
       toolStripButton1.Enabled = false;
       toolStripButton2.Enabled = false;
    
       // Trigger cancellation.
       cancellationSource.Cancel();
    
       try
       {
          // Asynchronously wait for the pipeline to complete processing and for
          // the progress bars to update.
          await Task.WhenAll(
             completeWork.Completion,
             incrementProgress.Completion,
             decrementProgress.Completion);
       }
       catch (OperationCanceledException)
       {
       }
    
       // Increment the progress bar that tracks the number of cancelled
       // work items by the number of active work items.
       toolStripProgressBar4.Value += toolStripProgressBar1.Value;
       toolStripProgressBar4.Value += toolStripProgressBar2.Value;
    
       // Reset the progress bars that track the number of active work items.
       toolStripProgressBar1.Value = 0;
       toolStripProgressBar2.Value = 0;
    
       // Enable the Add Work Items button.
       toolStripButton1.Enabled = true;
    }
    
    ' Event handler for the Cancel button.
    Private Async Sub toolStripButton2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton2.Click
        ' Disable both buttons.
        toolStripButton1.Enabled = False
        toolStripButton2.Enabled = False
    
        ' Trigger cancellation.
        cancellationSource.Cancel()
    
        Try
            ' Asynchronously wait for the pipeline to complete processing and for
            ' the progress bars to update.
            Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion, decrementProgress.Completion)
        Catch e1 As OperationCanceledException
        End Try
    
        ' Increment the progress bar that tracks the number of cancelled
        ' work items by the number of active work items.
        toolStripProgressBar4.Value += toolStripProgressBar1.Value
        toolStripProgressBar4.Value += toolStripProgressBar2.Value
    
        ' Reset the progress bars that track the number of active work items.
        toolStripProgressBar1.Value = 0
        toolStripProgressBar2.Value = 0
    
        ' Enable the Add Work Items button.
        toolStripButton1.Enabled = True
    End Sub
    

Esempio

L'esempio seguente illustra il codice completo per Form1.cs (Form1.vb per Visual Basic).

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

namespace CancellationWinForms
{
   public partial class Form1 : Form
   {
      // A placeholder type that performs work.
      class WorkItem
      {
         // Performs work for the provided number of milliseconds.
         public void DoWork(int milliseconds)
         {
            // For demonstration, suspend the current thread.
            Thread.Sleep(milliseconds);
         }
      }

      // Enables the user interface to signal cancellation.
      CancellationTokenSource cancellationSource;

      // The first node in the dataflow pipeline.
      TransformBlock<WorkItem, WorkItem> startWork;

      // The second, and final, node in the dataflow pipeline.
      ActionBlock<WorkItem> completeWork;

      // Increments the value of the provided progress bar.
      ActionBlock<ToolStripProgressBar> incrementProgress;

      // Decrements the value of the provided progress bar.
      ActionBlock<ToolStripProgressBar> decrementProgress;

      // Enables progress bar actions to run on the UI thread.
      TaskScheduler uiTaskScheduler;

      public Form1()
      {
         InitializeComponent();

         // Create the UI task scheduler from the current synchronization
         // context.
         uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
      }

      // Creates the blocks that participate in the dataflow pipeline.
      private void CreatePipeline()
      {
         // Create the cancellation source.
         cancellationSource = new CancellationTokenSource();

         // Create the first node in the pipeline.
         startWork = new TransformBlock<WorkItem, WorkItem>(workItem =>
         {
            // Perform some work.
            workItem.DoWork(250);

            // Decrement the progress bar that tracks the count of
            // active work items in this stage of the pipeline.
            decrementProgress.Post(toolStripProgressBar1);

            // Increment the progress bar that tracks the count of
            // active work items in the next stage of the pipeline.
            incrementProgress.Post(toolStripProgressBar2);

            // Send the work item to the next stage of the pipeline.
            return workItem;
         },
         new ExecutionDataflowBlockOptions
         {
            CancellationToken = cancellationSource.Token
         });

         // Create the second, and final, node in the pipeline.
         completeWork = new ActionBlock<WorkItem>(workItem =>
         {
            // Perform some work.
            workItem.DoWork(1000);

            // Decrement the progress bar that tracks the count of
            // active work items in this stage of the pipeline.
            decrementProgress.Post(toolStripProgressBar2);

            // Increment the progress bar that tracks the overall
            // count of completed work items.
            incrementProgress.Post(toolStripProgressBar3);
         },
         new ExecutionDataflowBlockOptions
         {
            CancellationToken = cancellationSource.Token,
            MaxDegreeOfParallelism = 2
         });

         // Connect the two nodes of the pipeline. When the first node completes,
         // set the second node also to the completed state.
         startWork.LinkTo(
            completeWork, new DataflowLinkOptions { PropagateCompletion = true });

         // Create the dataflow action blocks that increment and decrement
         // progress bars.
         // These blocks use the task scheduler that is associated with
         // the UI thread.

         incrementProgress = new ActionBlock<ToolStripProgressBar>(
            progressBar => progressBar.Value++,
            new ExecutionDataflowBlockOptions
            {
               CancellationToken = cancellationSource.Token,
               TaskScheduler = uiTaskScheduler
            });

         decrementProgress = new ActionBlock<ToolStripProgressBar>(
            progressBar => progressBar.Value--,
            new ExecutionDataflowBlockOptions
            {
               CancellationToken = cancellationSource.Token,
               TaskScheduler = uiTaskScheduler
            });
      }

      // Event handler for the Add Work Items button.
      private void toolStripButton1_Click(object sender, EventArgs e)
      {
         // The Cancel button is disabled when the pipeline is not active.
         // Therefore, create the pipeline and enable the Cancel button
         // if the Cancel button is disabled.
         if (!toolStripButton2.Enabled)
         {
            CreatePipeline();

            // Enable the Cancel button.
            toolStripButton2.Enabled = true;
         }

         // Post several work items to the head of the pipeline.
         for (int i = 0; i < 5; i++)
         {
            toolStripProgressBar1.Value++;
            startWork.Post(new WorkItem());
         }
      }

      // Event handler for the Cancel button.
      private async void toolStripButton2_Click(object sender, EventArgs e)
      {
         // Disable both buttons.
         toolStripButton1.Enabled = false;
         toolStripButton2.Enabled = false;

         // Trigger cancellation.
         cancellationSource.Cancel();

         try
         {
            // Asynchronously wait for the pipeline to complete processing and for
            // the progress bars to update.
            await Task.WhenAll(
               completeWork.Completion,
               incrementProgress.Completion,
               decrementProgress.Completion);
         }
         catch (OperationCanceledException)
         {
         }

         // Increment the progress bar that tracks the number of cancelled
         // work items by the number of active work items.
         toolStripProgressBar4.Value += toolStripProgressBar1.Value;
         toolStripProgressBar4.Value += toolStripProgressBar2.Value;

         // Reset the progress bars that track the number of active work items.
         toolStripProgressBar1.Value = 0;
         toolStripProgressBar2.Value = 0;

         // Enable the Add Work Items button.
         toolStripButton1.Enabled = true;
      }

      ~Form1()
      {
         cancellationSource.Dispose();
      }
   }
}
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Threading.Tasks.Dataflow


Namespace CancellationWinForms
    Partial Public Class Form1
        Inherits Form
        ' A placeholder type that performs work.
        Private Class WorkItem
            ' Performs work for the provided number of milliseconds.
            Public Sub DoWork(ByVal milliseconds As Integer)
                ' For demonstration, suspend the current thread.
                Thread.Sleep(milliseconds)
            End Sub
        End Class

        ' Enables the user interface to signal cancellation.
        Private cancellationSource As CancellationTokenSource

        ' The first node in the dataflow pipeline.
        Private startWork As TransformBlock(Of WorkItem, WorkItem)

        ' The second, and final, node in the dataflow pipeline.
        Private completeWork As ActionBlock(Of WorkItem)

        ' Increments the value of the provided progress bar.
        Private incrementProgress As ActionBlock(Of ToolStripProgressBar)

        ' Decrements the value of the provided progress bar.
        Private decrementProgress As ActionBlock(Of ToolStripProgressBar)

        ' Enables progress bar actions to run on the UI thread.
        Private uiTaskScheduler As TaskScheduler

        Public Sub New()
            InitializeComponent()

            ' Create the UI task scheduler from the current synchronization
            ' context.
            uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
        End Sub

        ' Creates the blocks that participate in the dataflow pipeline.
        Private Sub CreatePipeline()
            ' Create the cancellation source.
            cancellationSource = New CancellationTokenSource()

            ' Create the first node in the pipeline.
            startWork = New TransformBlock(Of WorkItem, WorkItem)(Function(workItem)
                                                                      ' Perform some work.
                                                                      ' Decrement the progress bar that tracks the count of
                                                                      ' active work items in this stage of the pipeline.
                                                                      ' Increment the progress bar that tracks the count of
                                                                      ' active work items in the next stage of the pipeline.
                                                                      ' Send the work item to the next stage of the pipeline.
                                                                      workItem.DoWork(250)
                                                                      decrementProgress.Post(toolStripProgressBar1)
                                                                      incrementProgress.Post(toolStripProgressBar2)
                                                                      Return workItem
                                                                  End Function,
            New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token})

            ' Create the second, and final, node in the pipeline.
            completeWork = New ActionBlock(Of WorkItem)(Sub(workItem)
                                                            ' Perform some work.
                                                            ' Decrement the progress bar that tracks the count of
                                                            ' active work items in this stage of the pipeline.
                                                            ' Increment the progress bar that tracks the overall
                                                            ' count of completed work items.
                                                            workItem.DoWork(1000)
                                                            decrementProgress.Post(toolStripProgressBar2)
                                                            incrementProgress.Post(toolStripProgressBar3)
                                                        End Sub,
            New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                    .MaxDegreeOfParallelism = 2})

            ' Connect the two nodes of the pipeline. When the first node completes,
            ' set the second node also to the completed state.
            startWork.LinkTo(
               completeWork, New DataflowLinkOptions With {.PropagateCompletion = true})

            ' Create the dataflow action blocks that increment and decrement
            ' progress bars.
            ' These blocks use the task scheduler that is associated with
            ' the UI thread.

            incrementProgress = New ActionBlock(Of ToolStripProgressBar)(
               Sub(progressBar) progressBar.Value += 1,
               New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                       .TaskScheduler = uiTaskScheduler})

            decrementProgress = New ActionBlock(Of ToolStripProgressBar)(
               Sub(progressBar) progressBar.Value -= 1,
               New ExecutionDataflowBlockOptions With {.CancellationToken = cancellationSource.Token,
                                                       .TaskScheduler = uiTaskScheduler})

        End Sub

        ' Event handler for the Add Work Items button.
        Private Sub toolStripButton1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton1.Click
            ' The Cancel button is disabled when the pipeline is not active.
            ' Therefore, create the pipeline and enable the Cancel button
            ' if the Cancel button is disabled.
            If Not toolStripButton2.Enabled Then
                CreatePipeline()

                ' Enable the Cancel button.
                toolStripButton2.Enabled = True
            End If

            ' Post several work items to the head of the pipeline.
            For i As Integer = 0 To 4
                toolStripProgressBar1.Value += 1
                startWork.Post(New WorkItem())
            Next i
        End Sub

        ' Event handler for the Cancel button.
        Private Async Sub toolStripButton2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles toolStripButton2.Click
            ' Disable both buttons.
            toolStripButton1.Enabled = False
            toolStripButton2.Enabled = False

            ' Trigger cancellation.
            cancellationSource.Cancel()

            Try
                ' Asynchronously wait for the pipeline to complete processing and for
                ' the progress bars to update.
                Await Task.WhenAll(completeWork.Completion, incrementProgress.Completion, decrementProgress.Completion)
            Catch e1 As OperationCanceledException
            End Try

            ' Increment the progress bar that tracks the number of cancelled
            ' work items by the number of active work items.
            toolStripProgressBar4.Value += toolStripProgressBar1.Value
            toolStripProgressBar4.Value += toolStripProgressBar2.Value

            ' Reset the progress bars that track the number of active work items.
            toolStripProgressBar1.Value = 0
            toolStripProgressBar2.Value = 0

            ' Enable the Add Work Items button.
            toolStripButton1.Enabled = True
        End Sub

        Protected Overrides Sub Finalize()
            cancellationSource.Dispose()
            MyBase.Finalize()
        End Sub
    End Class
End Namespace

Nell'immagine riportata di seguito viene illustrata l'applicazione in esecuzione.

The Windows Forms Application

Vedi anche