Vorgehensweise: Angeben eines Taskplaners in einem Datenflussblock
Dieses Dokument veranschaulicht, wie Sie einen bestimmten Taskplaner zuweisen, wenn Sie Datenfluss in Ihrer Anwendung verwenden. Das Beispiel verwendet die System.Threading.Tasks.ConcurrentExclusiveSchedulerPair-Klasse in einer Windows Forms-Anwendung, um anzuzeigen, wann die Readeraufgaben aktiv sind und wann eine Writeraufgabe aktiv ist. Darüber hinaus verwendet das Beispiel die TaskScheduler.FromCurrentSynchronizationContext-Methode, um zu ermögliche, dass ein Datenflussblock im Benutzeroberflächenthread ausgeführt wird.
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
Erstellen Sie ein Visual C#- oder Visual Basic-Windows Forms-Anwendungsprojekt. In den folgenden Schritten wird das Projekt
WriterReadersWinForms
benannt.Fügen Sie im Formulardesigner für das Hauptformular „Form1.cs“ („Form1.vb“ in Visual Basic) vier CheckBox-Steuerelemente hinzu. Legen Sie die Text-Eigenschaft auf Reader 1 für
checkBox1
, Reader 2 fürcheckBox2
, Reader 3 fürcheckBox3
und Writer fürcheckBox4
fest. Legen Sie die Enabled-Eigenschaft für jedes Steuerelement aufFalse
fest.Fügen Sie dem Formular ein Timer-Steuerelement hinzu. Setzen Sie die Interval-Eigenschaft auf
2500
.
Hinzufügen der Datenflussfunktionalität
In diesem Abschnitt wird beschrieben, wie Sie die Datenflussblöcke erstellen, die Teil der Anwendung sind, und wie Sie die einzelnen Blöcke einem Aufgabenplaner zuordnen.
Hinzufügen von Datenflussfunktionalität zur Anwendung
Fügen Sie dem Projekt einen Verweis auf „System.Threading.Tasks.Dataflow.dll“ hinzu.
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.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
Fügen Sie ein BroadcastBlock<T>-Datenmember zur
Form1
-Klasse hinzu.// 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)
Erstellen Sie im
Form1
-Konstuktor nach dem Aufruf vonInitializeComponent
ein ActionBlock<TInput>-Objekt, das den Status der CheckBox-Objekte wechselt.// 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()})
Erstellen Sie im
Form1
-Konstruktor ein ConcurrentExclusiveSchedulerPair-Objekt und vier ActionBlock<TInput>-Objekte, ein ActionBlock<TInput>-Objekt für jedes CheckBox-Objekt. Geben Sie für jedes ActionBlock<TInput>-Objekt ein ExecutionDataflowBlockOptions-Objekt an, für das die TaskScheduler-Eigenschaft auf die ConcurrentScheduler-Eigenschaft der Reader und die ExclusiveScheduler-Eigenschaft für den Writer festgelegt ist.// 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)
Starten Sie im
Form1
-Konstruktor das Timer-Objekt.// Start the timer. timer1.Start();
' Start the timer. timer1.Start()
Erstellen Sie im Formular-Designer für das Hauptformular einen Ereignishandler für das Tick-Ereignis des Timers.
Implementieren Sie das Tick-Ereignis für den Timer.
// 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
Da der toggleCheckBox
-Datenflussblock als Benutzeroberfläche agiert, ist es wichtig, dass diese Aktion im Benutzeroberflächenthread erfolgt. Um dies zu erreichen, stellt dieses Objekt während der Erstellung ein ExecutionDataflowBlockOptions-Objekt bereit, für das die TaskScheduler-Eigenschaft auf TaskScheduler.FromCurrentSynchronizationContext festgelegt ist. Die FromCurrentSynchronizationContext-Methode erstellt ein TaskScheduler-Objekt, das Arbeiten im aktuellen Synchronisierungskontext durchführt. Da der Form1
-Konstruktor über den Benutzeroberflächenthread aufgerufen wird, wird die Aktion für den toggleCheckBox
-Datenflussblock ebenfalls im Benutzeroberflächenthread ausgeführt.
Dieses Beispiel verwendet außerdem die ConcurrentExclusiveSchedulerPair-Klasse, um die gleichzeitige Ausführung einiger Datenflussblöcke zu aktivieren. Außerdem ermöglicht sie, dass ein anderer Datenflussblock exklusiv für alle anderen Datenflussblöcke ausgeführt wird, die auf dem gleichen ConcurrentExclusiveSchedulerPair-Objekt ausgeführt werden. Dieses Verfahren ist nützlich, wenn mehrere Datenflussblöcke eine Ressource gemeinsam nutzen und einige davon exklusiven Zugriff auf die Ressource benötigen, da es aufgrund dieses Verfahrens nicht mehr notwendig ist, den Zugriff auf diese Ressource manuell zu synchronisieren. Dadurch, dass die manuelle Synchronisierung entfällt, kann Code effizienter gestaltet werden.
Beispiel
Im folgenden Beispiel wird der vollständige Code für „Form1.cs“ („Form1.vb“ in Visual Basic) gezeigt.
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