Gör så här: Avbryt ett dataflödesblock
Det här dokumentet visar hur du aktiverar annullering i ditt program. Det här exemplet använder Windows Forms för att visa var arbetsobjekt är aktiva i en dataflödespipeline och även effekterna av annullering.
Kommentar
TPL-dataflödesbiblioteket ( System.Threading.Tasks.Dataflow namnområdet) distribueras inte med .NET. Om du vill installera System.Threading.Tasks.Dataflow namnområdet i Visual Studio öppnar du projektet, väljer Hantera NuGet-paket på Project-menyn och söker online efter System.Threading.Tasks.Dataflow
paketet. Du kan också installera den med .NET Core CLI genom att köra dotnet add package System.Threading.Tasks.Dataflow
.
Så här skapar du Windows Forms-programmet
Skapa ett C#- eller Visual Basic Windows Forms Application-projekt . I följande steg heter
CancellationWinForms
projektet .Lägg till en ToolStrip kontroll i formulärdesignern för huvudformuläret Form1.cs (Form1.vb för Visual Basic).
Lägg till en ToolStripButton kontroll i ToolStrip kontrollen. DisplayStyle Ange egenskapen till Text och egenskapen Text till Lägg till arbetsobjekt.
Lägg till en andra ToolStripButton kontroll i ToolStrip kontrollen. DisplayStyle Ange egenskapen till Text, egenskapen Text till Avbryt och egenskapen Enabled till
False
.Lägg till fyra ToolStripProgressBar objekt i ToolStrip kontrollen.
Skapa dataflödespipelinen
I det här avsnittet beskrivs hur du skapar dataflödespipelinen som bearbetar arbetsobjekt och uppdaterar förloppsstaplarna.
Skapa dataflödespipelinen
I projektet lägger du till en referens till System.Threading.Tasks.Dataflow.dll.
Se till att Form1.cs (Form1.vb för Visual Basic) innehåller följande
using
direktiv (Imports
i 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
WorkItem
Lägg till klassen som en inre typ avForm1
klassen.// 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
Lägg till följande datamedlemmar i
Form1
klassen.// 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
Lägg till följande metod,
CreatePipeline
, iForm1
klassen.// 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
Eftersom blocken incrementProgress
och decrementProgress
dataflödet fungerar i användargränssnittet är det viktigt att dessa åtgärder utförs i användargränssnittstråden. För att åstadkomma detta tillhandahåller dessa objekt under konstruktionen TaskScheduler ett ExecutionDataflowBlockOptions objekt som har egenskapen inställd på TaskScheduler.FromCurrentSynchronizationContext. Metoden TaskScheduler.FromCurrentSynchronizationContext skapar ett TaskScheduler objekt som utför arbete med den aktuella synkroniseringskontexten. Form1
Eftersom konstruktorn anropas från användargränssnittstråden körs även åtgärderna för blocken incrementProgress
och decrementProgress
dataflödesblocken i användargränssnittstråden.
Det här exemplet anger egenskapen CancellationToken när den konstruerar medlemmarna i pipelinen. Eftersom egenskapen permanent avbryter körningen CancellationToken av dataflödesblocket måste hela pipelinen återskapas när användaren har avbrutit åtgärden och sedan vill lägga till fler arbetsobjekt i pipelinen. Ett exempel som visar ett alternativt sätt att avbryta ett dataflödesblock så att annat arbete kan utföras när en åtgärd har avbrutits finns i Genomgång: Använda dataflöde i ett Windows-formulärprogram.
Ansluta dataflödespipelinen till användargränssnittet
I det här avsnittet beskrivs hur du ansluter dataflödespipelinen till användargränssnittet. Både att skapa pipelinen och lägga till arbetsobjekt i pipelinen styrs av händelsehanteraren för knappen Lägg till arbetsobjekt . Annullering initieras av knappen Avbryt . När användaren klickar på någon av dessa knappar initieras lämplig åtgärd på ett asynkront sätt.
Ansluta dataflödespipelinen till användargränssnittet
I formulärdesignern för huvudformuläret skapar du en händelsehanterare för Click händelsen för knappen Lägg till arbetsobjekt .
Click Implementera händelsen för knappen Lägg till arbetsobjekt.
// 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
I formulärdesignern för huvudformuläret skapar du en händelsehanterare för Click händelsehanteraren för knappen Avbryt .
Click Implementera händelsehanteraren för knappen Avbryt.
// 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
Exempel
I följande exempel visas den fullständiga koden för Form1.cs (Form1.vb för 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
Följande bild visar det program som körs.