Exportieren (0) Drucken
Alle erweitern
Dieser Artikel wurde manuell übersetzt. Bewegen Sie den Mauszeiger über die Sätze im Artikel, um den Originaltext anzuzeigen. Weitere Informationen
Übersetzung
Original

Aufgabenbasiertes asynchrones Muster (TAP, Task-based Asynchronous Pattern)

.NET Framework 4.5

Das aufgabenbasierte asynchrone Muster (TAP) basiert auf den Typen System.Threading.Tasks.Task und System.Threading.Tasks.Task<TResult> im System.Threading.Tasks-Namespace, mit denen beliebige asynchrone Vorgänge dargestellt werden. TAP ist das empfohlene Entwurfsmuster für asynchrone Neuentwicklungen.

TAP verwendet eine einfache Methode, um die Initiierung und den Abschluss eines asynchronen Vorgangs darzustellen. Dies steht im Gegensatz zum asynchronen Programmierungsmodell (APM oder IAsyncResult)-Muster, welches die Methoden Begin und End erfordert sowie im Gegensatz zum ereignisbasierten asynchronen Muster (EAP), das eine Methode mit Async-Suffix erfordert, sowie ein oder mehrere Ereignisse, Ereignishandler-Delegattypen und EventArg-abgeleitete Typen. Asynchrone Methoden im TAP umfassen das Suffix Async nach dem Vorgangsnamen; beispielsweise GetAsync für einen GET-Vorgang. Wenn Sie eine TAP-Methode zu einer Klasse hinzufügen, die bereits diese Methode mit dem Async-Suffix enthält, verwenden Sie stattdessen das Suffix TaskAsync. Wenn beispielsweise die Klasse bereits über eine GetAsync-Methode verfügt, verwenden Sie den Namen GetTaskAsync.

Die TAP-Methode gibt entweder System.Threading.Tasks.Task oder System.Threading.Tasks.Task<TResult> zurück. Das hängt davon ab, ob die entsprechende synchrone Methode void zurückgibt oder einen TResult-Typ.

Die Parameter einer TAP-Methode sollten mit den Parametern der synchronen Entsprechung übereinstimmen und in der gleichen Reihenfolge bereitgestellt werden. Diese Regel gilt jedoch nicht für den out-Parameter und den ref-Parameter. Diese Parameter sollten vollständig vermieden werden. Alle Daten, die über einen out-Parameter oder einen ref-Parameter zurückgegeben werden, sollten stattdessen als Teil des von Task<TResult> zurückgegebenen TResult zurückgegeben werden, wobei für die Verwendung mehrerer Werte ein Tupel oder eine benutzerdefinierte Datenstruktur genutzt wird. Methoden, die ausschließlich zur Erstellung, Bearbeitung oder Kombination von Aufgaben dienen (wobei die asynchrone Verwendung der Methode im Methodennamen oder im Namen des Typs, zu dem die Methode gehört, angegeben wird), müssen nach diesem Benennungsmuster benannt werden. Diese Methoden werden häufig als Kombinatoren bezeichnet. Beispiele für Kombinatoren umfassen WhenAll und WhenAny und werden im Verwenden von integrierten aufgabenbasierte Kombinatoren-Abschnitt des Artikels Verwenden des aufgabenbasierten asynchronen Musters erläutert.

Beispiele dafür, wie die TAP-Syntax sich von der Syntax in Legacyversionen des asynchronen Programmiermusters (beispielsweise dem asynchronen Programmiermodell (APM) und dem ereignisbasierten asynchronen Muster (EAP)) unterscheidet, finden Sie unter Muster für die asynchrone Programmierung.

Eine asynchrone Methode, die auf TAP basiert, kann eine kleine Menge an Arbeit synchron ausführen, beispielsweise das Überprüfen von Argumenten und das Initiieren des asynchronen Vorgangs, bevor sie die resultierende Aufgabe zurückgibt. Synchrone Arbeiten sollten auf ein Minimum beschränkt werden, damit die asynchrone Methode schnell zurückgeben kann. Gründe für eine schnelle Rückgabe umfassen Folgendes:

  • Asynchrone Methoden können von den Threads der Benutzeroberfläche aufgerufen werden und jede synchrone Arbeit mit langer Laufzeit kann die Reaktionszeit der Anwendung beeinträchtigen.

  • Mehrere asynchrone Methoden können gleichzeitig ausgelöst werden. Daher kann jede Arbeit mit langer Laufzeit im synchronen Teil einer asynchronen Methode die Initiierung anderer asynchroner Operationen verzögern und so sie die Vorteile der Nebenläufigkeit verringern.

In einigen Fällen ist der Arbeitsaufwand, der erforderlich ist, um den Vorgang abzuschließen, kleiner als der Arbeitsaufwand, der erforderlich ist, um den Vorgang asynchron zu starten. Lesen aus einem Stream, bei dem der Lesevorgang durch Daten erfüllt werden kann, die bereits im Arbeitsspeicher gepuffert werden, ist ein Beispiel für ein solches Szenario. In solchen Fällen wird der Vorgang möglicherweise synchron abgeschlossen, und eine bereits abgeschlossene Aufgabe kann zurückgegeben werden.

Eine asynchrone Methode sollte nur Ausnahmen auslösen, die aufgrund eines Verwendungsfehlers beim Aufruf der asynchronen Methode ausgelöst werden. Verwendungsfehler sollten nie im Produktionscode auftreten. Verursacht beispielsweise die Übergabe eines NULL-Verweises (Nothing in Visual Basic) als eines der Argumente der Methode einen Fehlerzustand (normalerweise dargestellt durch eine ArgumentNullException-Ausnahme), können Sie den Aufrufcode ändern, um sicherzustellen, dass nie ein NULL-Verweis übergeben wird. Für alle anderen Fehler sollten Ausnahmen, die beim Ausführen einer asynchronen Methode auftreten, der zurückgegebenen Aufgabe zugewiesen werden. Dies gilt auch dann, wenn die asynchrone Methode zufällig synchron abschließt, bevor die Aufgabe zurückgegeben wird. In der Regel enthält eine Aufgabe höchstens eine Ausnahme. Falls die Aufgabe jedoch mehrere Vorgänge darstellt (beispielsweise bei WhenAll), können einer einzelnen Aufgabe mehrere Ausnahmen zugeordnet werden.

Wenn Sie eine TAP-Methode implementieren, können Sie bestimmen, wo die asynchrone Ausführung auftritt. Sie können die Arbeitslast im Threadpool ausführen lassen, sie mithilfe einer asynchronen E/A implementieren (sodass sie für den Großteil der Ausführung des Vorgangs nicht an einen Thread gebunden ist), sie nach Bedarf in einem bestimmten Thread (z. B. dem UI-Thread) oder einer beliebigen Anzahl von anderen potenziellen Kontexten ausführen lassen. Es ist sogar möglich, dass eine TAP-Methode nichts ausführt und einen Task zurückgibt, der lediglich das Eintreten einer Bedingung an anderer Stelle im System darstellt (beispielsweise eine Aufgabe, die die Ankunft von Daten in einer Datenstruktur, die in einer Warteschlange eingereihten ist, darstellt). Der Aufrufer der TAP-Methode blockiert möglicherweise das Warten auf den Abschluss der TAP-Methode, indem er synchron auf die resultierende Aufgabe wartet oder er führt mithilfe von zusätzlichem (Fortsetzungs-)Code aus, sobald der asynchrone Vorgang abgeschlossen ist. Der Ersteller des Fortsetzungscodes steuert, wo dieser Code ausgeführt wird. Sie können diesen Fortsetzungscode entweder explizit durch Methoden der Task-Klasse (z. B. ContinueWith) oder implizit mithilfe von Sprachunterstützung erstellen, die auf Fortsetzungen aufbaut (zum Beispiel await in C#, Await in Visual Basic, AwaitValue in F#).

Die Task-Klasse stellt einen Lebenszyklus für asynchrone Vorgänge bereit, und dieser Zyklus wird durch die TaskStatus-Enumeration dargestellt. Für die Unterstützung von Ausnahmefällen von Typen, die von Task und Task<TResult> abgeleitet werden, sowie für die Unterstützung der Trennung von Erstellung und Planung macht die Task-Klasse eine Start-Methode verfügbar. Von den öffentlichen Task-Konstruktoren erstellte Aufgaben werden als inaktive Aufgaben bezeichnet, da ihr Lebenszyklus im nicht geplanten Zustand Created beginnt und sie sind erst im geplanten Zustand, wenn Start für diese Instanzen aufgerufen wird. Der Lebenszyklus aller anderen Aufgaben beginnt im aktiven Zustand. Dies bedeutet, dass die asynchronen Vorgänge, die sie darstellen, bereits initiiert wurden, und dass ihr Aufgabenzustand ein anderer Enumerationswert als TaskStatus.Created ist. Alle Aufgaben, die von TAP-Methoden zurückgegeben werden, müssen aktiviert sein. Wenn eine TAP-Methode intern die zurückzugebende Aufgabe mit dem Konstruktor der Aufgabe instanziiert, muss die TAP-Methode vor dem Zurückgeben der Aufgabe Start für das Task-Objekt aufrufen. Consumer einer TAP-Methode können davon ausgehen, dass die zurückgegebene Aufgabe aktiv ist, und sollten nicht versuchen, Start für einen von einer TAP-Methode zurückgegebenen Task aufzurufen. Der Aufruf von Start für eine aktive Aufgabe führt zu einer InvalidOperationException-Ausnahme.

Im TAP ist Abbruch sowohl für Implementierer asynchroner Methoden als auch für Consumer asynchroner Methoden optional. Wenn ein Vorgang einen Abbruch zulässt, macht er eine Überladung der asynchronen Methode verfügbar, die ein Abbruchtoken akzeptiert (CancellationToken-Instanz). Gemäß der Konvention lautet der Name des Parameters cancellationToken.

Der asynchrone Vorgang überwacht dieses Token auf Abbruchanforderungen. Wenn es eine Abbruchanforderung empfängt, kann es diese Anforderung erfüllen und den Vorgang abbrechen. Wenn die Abbruchanforderung dazu führt, dass die Arbeit vorzeitig beendet wird, gibt die TAP-Methode eine Aufgabe zurück, die im Canceled-Zustand beendet wird. Es ist kein Ergebnis verfügbar und keine Ausnahme wird ausgelöst. Der Zustand Canceled gilt als endgültiger (abgeschlossener) Zustand einer Aufgabe, ebenso wie die Zustände Faulted und RanToCompletion. Wenn eine Aufgabe im Zustand Canceled ist, gibt ihre IsCompleted-Eigenschaft true zurück. Wenn eine Aufgabe im Canceled-Zustand abgeschlossen wird, werden alle mit der Aufgabe registrierten Fortsetzungen geplant oder ausgeführt, es sei denn, eine Fortsetzungsmöglichkeit wie NotOnCanceled wurde angegeben, um die Fortsetzung zu beenden. Die Ausführung von jedem Code, der mithilfe von Sprachfunktionen asynchron auf eine abgebrochene Aufgabe wartet, wird weiter ausgeführt, aber der Code empfängt eine OperationCanceledException-Ausnahme oder eine von ihr abgeleitete Ausnahme. Synchron blockierter Code, der durch Methoden wie Wait und WaitAll auf die Aufgabe wartet, wird ebenso weiter mit einer Ausnahme ausgeführt.

Wenn ein Abbruchtoken den Abbruch angefordert hat, bevor die TAP-Methode, die dieses Token akzeptiert, aufgerufen wurde, sollte die TAP-Methode eine Canceled-Aufgabe zurückgeben. Wenn jedoch der Abbruch während der Ausführung des asynchronen Vorgangs angefordert wird, muss der asynchrone Vorgang die Abbruchanforderung nicht erfüllen. Die zurückgegebene Aufgabe sollte nur dann im Canceled-Zustand enden, wenn der Vorgang aufgrund einer Abbruchanforderung beendet wird. Wird der Abbruch angefordert aber dennoch ein Ergebnis oder eine Ausnahme erzeugt, sollte die Aufgabe im Zustand RanToCompletion oder Faulted enden. Für die asynchronen Methoden, die von einem Entwickler verwendet werden, dem es in erster Linie um einen Abbruch geht, müssen Sie keine Überladung bereitstellen, die kein Abbruchtoken akzeptiert. Für Methoden, die nicht abgebrochen werden können, sollten keine Überladungen bereitgestellt werden, die ein Abbruchstoken akzeptieren. So lässt sich leichter dem Aufrufer mitteilen, ob die Zielmethode tatsächlich abgebrochen werden kann. Consumercode, der keinen Abbruch wünscht, wird möglicherweise eine Methode aufrufen, die CancellationToken akzeptiert und None als den Argumentwert bereitstellen. None ist zu dem standardmäßigen CancellationToken funktional äquivalent.

Mehrere asynchrone Vorgänge profitieren von dem Bereitstellen von Statusbenachrichtigungen. Diese werden in der Regel verwendet, um eine Benutzeroberfläche mit Informationen zum Status der asynchronen Operation zu aktualisieren. In TAP wird der Status wird durch eine IProgress<T>-Schnittstelle behandelt, die der asynchronen Methode als Parameter übergeben wird, der normalerweise progress genannt wird. Durch das Bereitstellen der Statusschnittstelle zum Zeitpunkt des Aufrufs der asynchronen Methode lassen sich leichter Racebedingungen vermeiden, die aus falscher Verwendung resultieren (d. h., wenn Ereignishandler, die nach dem Aufruf des Vorgangs nicht ordnungsgemäß registriert wurden, möglicherweise Updates verpassen). Vor allem unterstützt die Statusschnittstelle jedoch verschiedene Implementierungen des Status, die durch den verwendeten Code bestimmt werden. Beispielsweise sollte der verwendete Code sich möglicherweise nur für das neueste Statusupdate interessieren oder alle Updates puffern oder eine Aktion für jedes Update aufrufen oder steuern, ob der Aufruf eines bestimmten Thread gemarshallt wird. Diese Optionen werden erreicht, indem eine andere Implementierung der Schnittstelle verwendet wird, die an bestimmte Anforderungen des Consumers angepasst wird. Wie bei einem Abbruch sollten auch TAP-Implementierungen nur dann einen IProgress<T>-Parameter bereitstellen, wenn die API Statusbenachrichtigungen unterstützt. Wenn beispielsweise die weiter oben in diesem Artikel beschriebene ReadAsync-Methode in der Lage ist, einen Zwischenstatus in Form der Anzahl von bisher gelesenen Bytes zu melden, könnte der Statusrückruf eine IProgress<T>-Schnittstelle sein:


public Task ReadAsync(byte[] buffer, int offset, int count, 
                      IProgress<long> progress)


Wenn eine FindFilesAsync-Methode eine Liste aller Dateien zurückgegeben hat, die einem bestimmten Suchmuster entsprechen, kann der Statusrückruf einen geschätzten Prozentsatz der abgeschlossenen Arbeit sowie den aktuellen Satz von partiellen Ergebnissen bereitstellen. Dies kann entweder mit einem Tupel erfolgen:


public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
            string pattern, 
            IProgress<Tuple<double, 
            ReadOnlyCollection<List<FileInfo>>>> progress)


oder mit einem für die API spezifischen Datentyp:


public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
    string pattern, 
    IProgress<FindFilesProgressInfo> progress)


In letzterem Fall wird an den speziellen Datentyp für gewöhnlich das Suffix ProgressInfo angefügt.

Wenn TAP-Implementierungen Überladungen bereitstellen, die einen Statusparameter akzeptieren, müssen sie den Wert progress für das Argument null zulassen. In diesem Fall wird kein Status gemeldet. TAP-Implementierungen sollten den Status synchron an das Progress<T>-Objekt melden, das die asynchrone Methode ermöglicht, um den Status schnell bereitzustellen und dem Consumer des Status ermöglichen, zu bestimmen wie und wo die Informationen am besten bearbeitet werden. Zum Beispiel kann die Statusinstanz Rückrufe marshallen und Ereignisse auf einem aufgezeichneten Synchronisierungskontext auslösen.

.NET Framework 4,5 stellt eine einzelne IProgress<T>-Implementierung bereit: Progress<T>. Die Progress<T>-Klasse wird folgendermaßen deklariert:


public class Progress<T> : IProgress<T>
{
    public Progress();
    public Progress(Action<T> handler);
    protected virtual void OnReport(T value);
    public event EventHandler<T> ProgressChanged;
}

Eine Instanz von Progress<T> macht ein ProgressChanged-Ereignis verfügbar, das jedes Mal ausgelöst wird, wenn der asynchrone Vorgang ein Statusupdate meldet. Das ProgressChanged-Ereignis wird auf das SynchronizationContext-Objekt ausgelöst, das aufgezeichnet wurde, als die Progress<T>-Instanz instanziiert wurde. Wenn kein Synchronisierungskontext verfügbar war, wird ein Standardkontext, der auf den Threadpool abzielt, verwendet. Handler können mit diesem Ereignis registriert werden. Ein einzelner Handler kann auch dem Progress<T>-Konstruktor nach Wunsch bereitgestellt werden und verhält sich wie ein Ereignishandler für das ProgressChanged-Ereignis. Statusupdates werden asynchron ausgelöst, um den asynchronen Vorgang zu verzögern, während der Ereignishandler ausgeführt wird. Eine andere IProgress<T>-Implementierung kann festlegen, dass andere Semantik anzuwenden ist.

Verwendet eine TAP-Implementierung sowohl den optionalen Parameter CancellationToken als auch den optionalen Parameter IProgress<T>, kann dies möglicherweise bis zu vier Überladungen erfordern:


public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
public Task MethodNameAsync(…, IProgress<T> progress); 
public Task MethodNameAsync(…, 
    CancellationToken cancellationToken, IProgress<T> progress);

Viele TAP-Implementierungen bieten jedoch weder Abbruchs- noch Statusfunktionen und erfordern daher eine einzelne Methode:

public Task MethodNameAsync(…);

Wenn eine TAP-Implementierung entweder Abbruch oder Status, jedoch nicht beides unterstützt, kann sie zwei Überladungen bereitstellen:


public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);

// … or …

public Task MethodNameAsync(…);
public Task MethodNameAsync(…, IProgress<T> progress);

Wenn eine TAP-Implementierung Abbruch und Status unterstützt, kann sie alle vier Überladungen verfügbar machen. Sie kann jedoch möglicherweise nur die Folgenden zwei bereitstellen:


public Task MethodNameAsync(…);
public Task MethodNameAsync(…, 
    CancellationToken cancellationToken, IProgress<T> progress);

Als Ausgleich für die zwei fehlenden Zwischenkombinationen können Entwickler None oder ein standardmäßiges CancellationToken für den cancellationToken-Parameter und null für den progress-Parameter übergeben.

Wenn Sie erwarten, dass jede Verwendung der TAP-Methode Abbruch oder Status unterstützt, können Sie die Überladungen, die den relevanten Parameter nicht akzeptieren, weglassen.

Wenn Sie sich dazu entschließen, mehrere Überladungen verfügbar zu machen, um Abbruch und/oder Status optional zu machen, sollte das Verhalten der Überladungen, die Abbruch und/oder Status nicht unterstützen, dem Verhalten einer Überladung entsprechen, die null für Abbruch oder None für Status an die Überladung übergibt, die Abbruch bzw. Status unterstützt.

Titel

Beschreibung

Muster für die asynchrone Programmierung

Stellt die drei Muster zum Ausführen von asynchronen Vorgängen vor: das aufgabenbasierte asynchrone Muster (TAP), das asynchrone Programmiermodell (APM) und das ereignisbasierte asynchrone Muster (EAP).

Implementieren des aufgabenbasierten asynchronen Entwurfsmusters

Beschreibt, wie Sie das aufgabenbasierte asynchrone Muster (Task-based Asynchronous Pattern, TAP) auf drei Arten implementieren können: mit C# und den Visual Basic-Compilern in Visual Studio, manuell oder mit einer Kombination von Compilermethoden und manuellen Methoden.

Verwenden des aufgabenbasierten asynchronen Musters

Beschreibt, wie Sie Aufgaben und Rückrufe verwenden können, um eine Verzögerung ohne Blockierung zu erreichen.

Interoperabilität mit anderen asynchronen Mustern und Typen

Beschreibt, wie das aufgabenbasierte asynchrone Muster (TAP) verwendet werden kann, um das asynchrone Programmiermodell (APM) und das ereignisbasierte asynchrone Muster (EAP) zu implementieren.

Community-Beiträge

HINZUFÜGEN
Anzeigen:
© 2015 Microsoft