Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
V .NET je vzor asynchronního návrhu založený na úlohách doporučeným vzorem asynchronního návrhu pro nový vývoj. Je založená na typech TaskTask<TResult> v System.Threading.Tasks oboru názvů, které se používají k reprezentaci asynchronních operací.
Pojmenování, parametry a návratové typy
TAP používá jedinou metodu k reprezentaci zahájení a dokončení asynchronní operace. To kontrastuje se vzorem asynchronního programovacího modelu (APM) a IAsyncResult
asynchronním vzorem založeným na událostech (EAP). APM vyžaduje Begin
a End
metody. Protokol EAP vyžaduje metodu s příponou Async
a také vyžaduje jednu nebo více událostí, typy delegátů pro obsluhu událostí a typy odvozené od EventArg
. Asynchronní metody v TAP zahrnují příponu Async
za názvem operace pro metody, které vracejí očekávané typy, například Task, Task<TResult>, ValueTaska ValueTask<TResult>. Například asynchronní Get
operace, která vrací Task<String>
hodnotu lze pojmenovat GetAsync
. Pokud přidáváte metodu TAP do třídy, která už obsahuje název metody EAP s Async
příponou, použijte místo toho příponu TaskAsync
. Například pokud třída již má metodu GetAsync
, použijte název GetTaskAsync
. Pokud metoda spustí asynchronní operaci, ale nevrací typ, na který lze čekat, její název by měl začínat znakem Begin
, Start
, nebo jiným slovesem tak, aby naznačoval, že tato metoda nevrací ani nevyhodí výsledek operace.
Metoda TAP vrátí buď System.Threading.Tasks.Task, nebo System.Threading.Tasks.Task<TResult> v závislosti na tom, zda odpovídající synchronní metoda vrací void nebo typ TResult
.
Parametry metody TAP by měly odpovídat parametrům synchronního protějšku a měly by být poskytnuty ve stejném pořadí. Parametry out
a ref
jsou však z tohoto pravidla vyloučeny a měly by být zcela vynechány. Všechna data, která by byla vrácena prostřednictvím parametrů out
nebo ref
, by měla být vrácena jako součást TResult
vráceného metodou Task<TResult> a měla by použít n-tici nebo vlastní datovou strukturu, aby bylo možné zahrnout více hodnot. Zvažte také přidání parametru CancellationToken i v případě, že synchronní protějšek metody TAP ho nenabízí.
Metody, které jsou vyhrazeny výhradně pro vytváření, manipulaci nebo kombinaci úloh (kde asynchronní záměr metody je jasný v názvu metody nebo v názvu typu, do kterého metoda patří), nemusí postupovat podle tohoto vzoru pojmenování; tyto metody se často označují jako kombinátory. Příklady kombinátorů zahrnují WhenAll a WhenAnya jsou popsány v části Použití předdefinovaných kombinátorů založených na úlohách článku Využívání asynchronního vzoru založeného na úlohách.
Příklady, jak se syntaxe TAP liší od syntaxe používané ve starších vzorech asynchronního programování, jako je asynchronní programovací model (APM) a asynchronní vzor založený na událostech (EAP), najdete v tématu Asynchronní programovací vzory.
Iniciování asynchronní operace
Asynchronní metoda, která je založená na TAP, může provést malé množství práce synchronně, například ověření argumentů a zahájení asynchronní operace, než vrátí výslednou úlohu. Synchronní práce by měla být zachována na minimum, aby asynchronní metoda se rychle vrátila. Mezi důvody rychlého vrácení patří:
Asynchronní metody mohou být vyvolány z vláken uživatelského rozhraní (UI) a jakákoli dlouhotrvající synchronní práce může poškodit odezvu aplikace.
Souběžně může být spuštěno více asynchronních metod. Proto by jakákoli dlouhotrvající práce v synchronní části asynchronní metody mohla zpozdit zahájení jiných asynchronních operací, čímž se sníží výhody souběžnosti.
V některých případech je množství práce potřebné k dokončení operace menší než množství práce potřebné k asynchronnímu spuštění operace. Čtení z datového proudu, kde operace čtení může být splněna daty, která jsou již uložena do vyrovnávací paměti, je příkladem takového scénáře. V takových případech se operace může dokončit synchronně a může vrátit úkol, který už byl dokončen.
Výjimky
Asynchronní metoda by měla vyvolat výjimku, kterou je možné vyhodit z asynchronní metody pouze v reakci na chybu použití. Chyby použití by se nikdy neměly vyskytovat v produkčním kódu. Pokud například předáte odkaz null (Nothing
v jazyce Visual Basic) jako jeden z argumentů metody způsobí chybový stav (obvykle reprezentovaný ArgumentNullException výjimkou), můžete upravit volající kód tak, aby se zajistilo, že odkaz null nikdy nepřejde. U všech ostatních chyb by výjimky, ke kterým dochází při spuštění asynchronní metody, měly být přiřazeny k úloze, která se vrací, i když se asynchronní metoda dokončí synchronně před vrácením úlohy. Úloha obvykle obsahuje maximálně jednu výjimku. Pokud však úkol představuje více operací (například WhenAll), může být k jednomu úkolu přidruženo více výjimek.
Cílové prostředí
Při implementaci metody TAP můžete určit, kde dochází k asynchronnímu spuštění. Můžete se rozhodnout spustit úlohu ve fondu vláken, implementovat ji pomocí asynchronních vstupně-výstupních operací (bez vazby na vlákno pro většinu provádění operace), spustit ji na konkrétním vlákně (například vlákně uživatelského rozhraní) nebo použít libovolný počet potenciálních kontextů. Metoda TAP dokonce nemusí mít co spouštět a může jen vrátit Task, které představuje výskyt podmínky někde jinde v systému (například úloha představující data přicházející do fronty datové struktury).
Volající metody TAP může blokovat čekání na dokončení metody TAP synchronním čekáním na výslednou úlohu nebo může po dokončení asynchronní operace spustit další kód (pokračování). Tvůrce kódu pokračování má kontrolu nad tím, kde se tento kód spouští. Kód pokračování můžete vytvořit explicitně prostřednictvím metod třídy Task (například ContinueWith) nebo implicitně pomocí podpory jazyka založené na pokračováních (například await
v jazyce C#, Await
v jazyce Visual Basic AwaitValue
v jazyce F#).
Stav úkolu
Třída Task poskytuje životní cyklus pro asynchronní operace a tento cyklus je reprezentován výčtem TaskStatus . Chcete-li podporovat okrajové případy typů odvozených od Task a Task<TResult> a oddělit konstrukci od plánování, třída Task poskytuje metodu Start. Úlohy vytvořené veřejnými Task konstruktory jsou označovány jako studené úlohy, protože jejich životní cyklus začíná v neplánovaném Created stavu a jsou naplánovány pouze v případě, že Start jsou volány na těchto instancích.
Všechny ostatní úkoly začínají svůj životní cyklus v horkém stavu, což znamená, že asynchronní operace, které představují, již byly zahájeny a jejich stav úkolu je hodnota výčtu jiná než TaskStatus.Created. Všechny úkoly vrácené z metod TAP musí být aktivovány. Pokud metoda TAP interně používá konstruktor úlohy k vytvoření instance úlohy, která má být vrácena, musí metoda TAP zavolat Start na objekt Task předtím, než ho vrátí. Uživatelé metody TAP mohou bezpečně předpokládat, že vrácený úkol je aktivní a neměli by se pokoušet volat na žádné Start, které je vráceno metodou TAP. Volání Start na aktivním úkolu způsobí InvalidOperationException výjimku.
Zrušení (volitelné)
V TAP je zrušení volitelné pro implementátory asynchronních metod i příjemce asynchronní metody. Pokud operace umožňuje zrušení, zveřejňuje přetížení asynchronní metody, která přijímá token zrušení (CancellationToken instance). Podle konvence má parametr název cancellationToken
.
public Task ReadAsync(byte [] buffer, int offset, int count,
CancellationToken cancellationToken)
Public Function ReadAsync(buffer() As Byte, offset As Integer,
count As Integer,
cancellationToken As CancellationToken) _
As Task
Asynchronní operace monitoruje tento token kvůli žádostem o zrušení. Pokud obdrží žádost o zrušení, může se rozhodnout tuto žádost respektovat a operaci zrušit. Pokud se žádost o zrušení ukončí předčasně, vrátí metoda TAP úlohu, která končí ve Canceled stavu; neexistuje žádný dostupný výsledek a nevyvolá se žádná výjimka. Stav Canceled se považuje za konečný (dokončený) stav úkolu spolu se Faulted stavy a RanToCompletion stavy. Proto pokud je úkol ve Canceled stavu, jeho IsCompleted vlastnost vrátí true
. Po dokončení úkolu ve Canceled stavu jsou všechna pokračování zaregistrovaná u úkolu naplánovaná nebo spuštěná, pokud není zadána možnost pokračování, jako NotOnCanceled je například možnost vyřazení z pokračování. Veškerý kód, který asynchronně čeká na zrušenou úlohu pomocí funkcí jazyka, se bude dál spouštět, ale obdrží výjimku OperationCanceledException nebo výjimku odvozenou z ní. Kód, který je synchronně blokován při čekání na úlohu prostřednictvím metod, jako jsou Wait a WaitAll, také pokračuje ve spuštění s výjimkou.
Pokud bylo požádáno o zrušení pomocí tokenu zrušení před zavoláním metody TAP, která přijímá tento token, měla by metoda TAP vrátit úlohu Canceled. Pokud je však při spuštění asynchronní operace požadováno zrušení, asynchronní operace nemusí žádost o zrušení přijmout. Vrácený úkol by měl končit ve Canceled stavu pouze v případě, že operace skončí v důsledku požadavku na zrušení. Pokud je požadováno zrušení, ale výsledek nebo výjimka je stále vytvořena, úkol by měl končit ve stavu RanToCompletion nebo Faulted.
Pro asynchronní metody, které chtějí především zpřístupnit schopnost být zrušeny, nemusíte poskytovat přetížení, které nepřijímá rušící token. Pro metody, které nelze zrušit, neposkytujte přetížení, které přijímají token zrušení; to pomáhá označit volajícímu, zda je cílová metoda skutečně zrušitelná. Spotřebitelský kód, který nechce zrušení, může volat metodu, která přijímá CancellationToken a poskytuje None jako hodnotu argumentu. None je funkčně ekvivalentní výchozímu CancellationToken.
Vykazování průběhu (volitelné)
Některé asynchronní operace těží z poskytování oznámení o průběhu; obvykle slouží k aktualizaci uživatelského rozhraní s informacemi o průběhu asynchronní operace.
V TAP se průběh zpracovává prostřednictvím IProgress<T> rozhraní, které se předává asynchronní metodě jako parametr, který je obvykle pojmenován progress
. Poskytnutí rozhraní průběhu, když je volána asynchronní metoda, pomáhá eliminovat podmínky závodu, které jsou výsledkem nesprávného použití. To znamená, že obslužné rutiny událostí, které jsou nesprávně registrovány po spuštění operace, mohou nezaznamenávat aktualizace. Důležitější je, že rozhraní průběhu podporuje různé implementace průběhu, jak je určeno využíváním kódu. Příkladně může spotřebovávaný kód se zajímat pouze o nejnovější aktualizaci průběhu, nebo chtít ukládat všechny aktualizace do vyrovnávací paměti, nebo chtít vyvolat akci pro každou aktualizaci, nebo mít zájem na tom, zda je vyvolání zařazováno do určitého vlákna. Všechny tyto možnosti lze dosáhnout pomocí jiné implementace rozhraní, které je přizpůsobené potřebám konkrétního příjemce. Stejně jako u zrušení by implementace TAP měly poskytovat IProgress<T> parametr pouze v případě, že rozhraní API podporuje oznámení o průběhu.
Pokud je metoda ReadAsync
, o které se mluvilo dříve v tomto článku, schopna hlásit průběžný stav ve formě dosud přečteného počtu bajtů, může být zpětné volání pro průběh rozhraním IProgress<T>.
public Task ReadAsync(byte[] buffer, int offset, int count,
IProgress<long> progress)
Public Function ReadAsync(buffer() As Byte, offset As Integer,
count As Integer,
progress As IProgress(Of Long)) As Task
FindFilesAsync
Pokud metoda vrátí seznam všech souborů, které splňují určitý vzor hledání, zpětné volání průběhu může poskytnout odhad procenta dokončené práce a aktuální sadu částečných výsledků. Tyto informace by mohlo poskytnout buď jako n-tice:
public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<Tuple<double,
ReadOnlyCollection<List<FileInfo>>>> progress)
Public Function FindFilesAsync(pattern As String,
progress As IProgress(Of Tuple(Of Double, ReadOnlyCollection(Of List(Of FileInfo))))) _
As Task(Of ReadOnlyCollection(Of FileInfo))
nebo s datovým typem, který je specifický pro rozhraní API:
public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<FindFilesProgressInfo> progress)
Public Function FindFilesAsync(pattern As String,
progress As IProgress(Of FindFilesProgressInfo)) _
As Task(Of ReadOnlyCollection(Of FileInfo))
V druhém případě je speciální datový typ obvykle příponou ProgressInfo
.
Pokud implementace TAP poskytují přetížení, která přijímají parametr progress
, musí povolit, aby argument mohl být null
; v takovém případě není hlášen žádný pokrok. Implementace TAP by měly hlásit průběh Progress<T> objektu synchronně, což umožňuje asynchronní metodě rychle poskytnout průběh. Umožňuje také příjemci průběhu určit, jak a kde nejlépe zpracovávat informace. Instance sledování průběhu se například může rozhodnout usměrňovat zpětná volání a vyvolávat události v zachyceném kontextu synchronizace.
Implementace pro IProgress<T>
.NET poskytuje Progress<T> třídu, která implementuje IProgress<T>. Třída Progress<T> je deklarována takto:
public class Progress<T> : IProgress<T>
{
public Progress();
public Progress(Action<T> handler);
protected virtual void OnReport(T value);
public event EventHandler<T>? ProgressChanged;
}
Instance Progress<T> zveřejňuje ProgressChanged událost, která se vyvolá pokaždé, když asynchronní operace hlásí aktualizaci průběhu. Událost ProgressChanged je vyvolána u objektu SynchronizationContext, který byl zachycen při vytvoření instance Progress<T>. Pokud nebyl k dispozici žádný kontext synchronizace, použije se výchozí kontext, který cílí na fond vláken. Obslužné rutiny mohou být registrovány k této události. Jedna obslužná rutina může být konstruktoru Progress<T> k dispozici pro usnadnění a chová se stejně jako obslužná rutina události ProgressChanged. Aktualizace průběhu jsou vyvolány asynchronně, aby se zabránilo zpoždění asynchronní operace během zpracovávání událostí. Jiná IProgress<T> implementace by se mohla rozhodnout použít jinou sémantiku.
Volba přetížení, která se mají poskytnout
Pokud implementace TAP používá volitelné CancellationToken i volitelné IProgress<T> parametry, může to potenciálně vyžadovat až čtyři přetížení:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
public Task MethodNameAsync(…, IProgress<T> progress);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken cancellationToken) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
Mnoho implementací TAP neposkytuje ale možnosti zrušení ani sledování průběhu, takže vyžadují jednu metodu.
public Task MethodNameAsync(…);
Public MethodNameAsync(…) As Task
Pokud implementace TAP podporuje zrušení nebo průběh, ale ne obojí, může poskytnout dvě přetížení:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
// … or …
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken) As Task
' … or …
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Pokud implementace TAP podporuje zrušení i průběh, může zveřejnit všechna čtyři přetížení. Může však poskytnout pouze následující dvě položky:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
Aby vývojáři mohli tyto dvě chybějící přechodné kombinace kompenzovat, mohou předat None parametru None nebo výchozí hodnotu cancellationToken
, a null
parametru progress
.
Pokud očekáváte, že každé použití metody TAP podporuje zrušení nebo průběh, můžete vynechat přetížení, která nepřijímají příslušný parametr.
Pokud se rozhodnete zpřístupnit více přetížení, aby bylo zrušení nebo průběh volitelné, pak by se přetížení, která nepodporují zrušení nebo průběh, měla chovat tak, jako by předávala None pro zrušení nebo null
pro průběh do přetížení, které je podporují.
Související články
Titulek | Popis |
---|---|
Asynchronní programovací vzory | Představuje tři vzory pro provádění asynchronních operací: asynchronní vzor založený na úlohách (TAP), asynchronní programovací model (APM) a asynchronní vzor založený na událostech (EAP). |
Implementace asynchronního vzoru založeného na úlohách | Popisuje, jak implementovat asynchronní vzor založený na úlohách (TAP) třemi způsoby: pomocí kompilátorů jazyka C# a Visual Basic v sadě Visual Studio, ručně nebo kombinací kompilátoru a ručních metod. |
Využívání asynchronního vzoru založeného na úlohách | Popisuje, jak můžete pomocí úloh a zpětných volání dosáhnout čekání bez blokování. |
Interoperabilita s jinými asynchronními vzory a typy | Popisuje použití asynchronního vzoru založeného na úlohách (TAP) k implementaci asynchronního programovacího modelu (APM) a asynchronního vzoru založeného na událostech (EAP). |