Freigeben über


Vorgehensweise: Abbrechen eines Datenflussblocks

Dieses Dokument veranschaulicht, wie Sie das Abbrüche in der Anwendung aktivieren. In diesem Beispiel wird Windows Forms verwendet, um anzuzeigen, wo in einer Datenflusspipeline Arbeitselemente aktiv sind, und um die Auswirkungen eines Abbruchs zu verdeutlichen.

Hinweis

Die TPL-Datenflussbibliothek (System.Threading.Tasks.Dataflow-Namespace) wird nicht mit .NET ausgeliefert. Öffnen Sie zum Installieren des System.Threading.Tasks.Dataflow-Namespace in Visual Studio Ihr Projekt, wählen Sie im Menü Projekt die Option NuGet-Pakete verwalten aus, und suchen Sie online nach dem System.Threading.Tasks.Dataflow-Paket. Alternativ können Sie es mithilfe der .NET Core-CLI installieren und dazu dotnet add package System.Threading.Tasks.Dataflow ausführen.

Erstellen der Windows Forms-Anwendung

  1. Erstellen Sie ein C#- oder Visual Basic-Windows Forms-Anwendungsprojekt. In den folgenden Schritten wird das Projekt CancellationWinForms benannt.

  2. Fügen Sie im Formulardesigner für das Hauptformular „Form1.cs“ („Form1.vb“ in Visual Basic) ein ToolStrip-Steuerelement hinzu.

  3. Fügen Sie dem ToolStrip-Steuerelement ein ToolStripButton-Steuerelement hinzu. Legen Sie die DisplayStyle-Eigenschaft auf Text und die Text-Eigenschaft auf Arbeitselemente hinzufügen fest.

  4. Fügen Sie dem ToolStrip-Steuerelement ein zweites ToolStripButton-Steuerelement hinzu. Legen Sie die DisplayStyle-Eigenschaft auf Text, die Text-Eigenschaft auf Abbrechen und die Enabled-Eigenschaft auf False fest.

  5. Fügen Sie vier ToolStripProgressBar -Objekte zum ToolStrip-Steuerelement hinzu.

Erstellen der Datenflusspipeline

In diesem Abschnitt wird beschrieben, wie Sie die Datenflusspipeline erstellen, die Arbeitselemente verarbeitet und Statusanzeigen aktualisiert.

So erstellen Sie die Datenflusspipeline

  1. Fügen Sie dem Projekt einen Verweis auf „System.Threading.Tasks.Dataflow.dll“ hinzu.

  2. Stellen Sie sicher, dass Form1.cs (Form1.vb für Visual Basic) die folgenden using-Direktiven (Imports in Visual Basic) enthält.

    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. Fügen Sie die WorkItem-Klasse als einen inneren Typ der Form1-Klasse hinzu.

    // 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. Fügen Sie der Form1-Klasse die folgenden Datenmember hinzu.

    // 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. Fügen Sie die folgende CreatePipeline-Methode zu der Form1-Klasse hinzu.

    // 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
    

Da sich die incrementProgress- und decrementProgress-Datenflussblöcke auf die Benutzeroberfläche auswirken, ist es wichtig, dass diese Aktionen im Benutzeroberflächenthread erfolgen. Um dies zu erreichen, stellen diese Objekte während der Erstellung ein ExecutionDataflowBlockOptions-Objekt bereit, für das die TaskScheduler-Eigenschaft auf TaskScheduler.FromCurrentSynchronizationContext festgelegt ist. Die TaskScheduler.FromCurrentSynchronizationContext-Methode erstellt ein TaskScheduler-Objekt, das Arbeiten im aktuellen Synchronisierungskontext durchführt. Da der Form1-Konstruktor über den Benutzeroberflächenthread aufgerufen wird, werden die Aktionen für den incrementProgress- und den decrementProgress-Datenflussblock ebenfalls im Benutzeroberflächenthread ausgeführt.

In diesem Beispiel wird die CancellationToken-Eigenschaft festgelegt, wenn sie die Elemente der Pipeline erstellt. Da die CancellationToken-Eigenschaft die Ausführung des Datenflussblocks dauerhaft abbricht, muss die gesamte Pipeline neu erstellt werden, wenn der Benutzer den Vorgang abbricht und anschließend weitere Arbeitselemente zur Pipeline hinzufügen möchte. Ein Beispiel für eine alternative Möglichkeit zum Abbrechen eines Datenflussblocks, die erlaubt, dass nach dem Abbrechen eines Vorgangs andere Arbeit ausgeführt wird, finden Sie unter Exemplarische Vorgehensweise: Verwenden eines Datenflusses in einer Windows Forms-Anwendung.

Verbinden der Datenflusspipeline mit der Benutzeroberfläche

In diesem Abschnitt wird beschrieben, wie Sie die Datenflusspipeline mit der Benutzeroberfläche verbinden. Sowohl das Erstellen der Pipeline als auch das Hinzufügen von Arbeitselementen zur Pipeline werden vom Ereignishandler für die Schaltfläche Arbeitselemente hinzufügen gesteuert. Der Abbruch wird durch die Schaltfläche Abbrechen initiiert. Wenn der Benutzer auf eine dieser Schaltflächen klickt, wird die entsprechende Aktion auf asynchrone Weise initiiert.

So verbinden Sie die Datenflusspipeline mit der Benutzeroberfläche

  1. Erstellen Sie im Formulardesigner für das Hauptformular einen Ereignishandler für das Click-Ereignis der Schaltfläche Arbeitselemente hinzufügen.

  2. Implementieren Sie das Click-Ereignis für die Schaltfläche Arbeitselemente hinzufügen.

    // 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. Erstellen Sie im Formulardesigner für das Hauptformular einen Ereignishandler für den Click-Ereignishandler der Schaltfläche Abbrechen.

  4. Implementieren Sie den Click-Ereignishandler für die Schaltfläche Abbrechen.

    // 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
    

Beispiel

Im folgenden Beispiel wird der vollständige Code für „Form1.cs“ („Form1.vb“ in Visual Basic) gezeigt.

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

Die folgende Abbildung zeigt die ausgeführte Anwendung.

Windows Forms-Anwendung

Siehe auch