Sdílet prostřednictvím


Model asynchronního programování úloh

Pomocí asynchronního programování se můžete vyhnout kritickým bodům a zlepšit celkovou rychlost reakce aplikace. Tradiční techniky pro psaní asynchronních aplikací však mohou být složité, takže je obtížné je napsat, ladit a udržovat.

Jazyk C# podporuje zjednodušený přístup, asynchronní programování, které využívá asynchronní podporu v modulu runtime .NET. Kompilátor na sebe přejímá náročnou práci, kterou vykonával vývojář, a vaše aplikace si zachovává logickou strukturu, která se podobá synchronnímu kódu. Výsledkem je, že získáte všechny výhody asynchronního programování při pouhém zlomku úsilí.

Toto téma obsahuje přehled kdy a jak použít asynchronní programování a obsahuje odkazy na témata podpory, která obsahují podrobné informace a příklady.

Asynchronní funkce zlepšuje rychlost odezvy.

Asynchronní synchronizace je nezbytná pro aktivity, které mohou blokovat, například webový přístup. Přístup k webovému prostředku je někdy pomalý nebo zpožděný. Pokud je taková aktivita zablokovaná v synchronním procesu, musí celá aplikace počkat. U asynchronního procesu může aplikace pokračovat v další práci, která nezávisí na webovém prostředku, dokud neskončí potenciálně blokující úloha.

Následující tabulka ukazuje typické oblasti, kde asynchronní programování zlepšuje rychlost reakce. Uvedená rozhraní API z .NET a prostředí Windows Runtime obsahují metody, které podporují asynchronní programování.

Oblast aplikace Typy .NET s asynchronními metodami prostředí Windows Runtime typy pomocí asynchronních metod
Webový přístup HttpClient Windows.Web.Http.HttpClient
SyndicationClient
Práce se soubory JsonSerializer
StreamReader
StreamWriter
XmlReader
XmlWriter
StorageFile
Práce s obrázky MediaCapture
BitmapEncoder
BitmapDecoder
Programování WCF Synchronní a asynchronní operace

Asynchronie je obzvláště užitečná pro aplikace, které přistupují k vláknu UI, protože všechny aktivity související s uživatelským rozhraním obvykle sdílí jedno vlákno. Pokud je jakýkoli proces blokován v synchronní aplikaci, jsou blokovány všechny. Vaše aplikace přestane reagovat a můžete dojít k závěru, že selhala, i když místo toho čeká.

Při použití asynchronních metod bude aplikace i nadále odpovídat na uživatelské rozhraní. Pokud nechcete čekat na dokončení, můžete změnit velikost nebo minimalizovat okno, například můžete zavřít aplikaci.

Asynchronní přístup přidává ekvivalent automatického přenosu do seznamu možností, z nichž můžete vybírat při vytváření asynchronní operace. To znamená, že získáte všechny výhody tradičního asynchronního programování, ale s mnohem menším úsilím ze strany vývojáře.

Asynchronní metody se snadno zapisují

Asynchronní a očekávaná klíčová slova v jazyce C# jsou jádrem asynchronního programování. Pomocí těchto dvou klíčových slov můžete použít prostředky v rozhraní .NET Framework, .NET Core nebo prostředí Windows Runtime k vytvoření asynchronní metody téměř stejně snadno jako při vytváření synchronní metody. Asynchronní metody, které definujete pomocí klíčového async slova, se označují jako asynchronní metody.

Následující příklad ukazuje asynchronní metodu. Téměř všechno v kódu by mělo vypadat dobře.

Kompletní příklad Windows Presentation Foundation (WPF) je k dispozici ke stažení z asynchronního programování s asynchronním programováním a v jazyce C#.

public async Task<int> GetUrlContentLengthAsync()
{
    using var client = new HttpClient();

    Task<string> getStringTask =
        client.GetStringAsync("https://learn.microsoft.com/dotnet");

    DoIndependentWork();

    string contents = await getStringTask;

    return contents.Length;
}

void DoIndependentWork()
{
    Console.WriteLine("Working...");
}

Z předchozí ukázky se můžete seznámit s několika postupy. Začněte podpisem metody. async Obsahuje modifikátor. Návratový typ je Task<int> (další možnosti najdete v části Návratové typy). Název metody končí .Async V těle metody GetStringAsync vrátí hodnotu Task<string>. To znamená, že když úkol dostanete awaitstring (contents). Před čekáním na úkol můžete udělat práci, která nespoléhá na danou GetStringAsyncmožnost string .

Věnujte operátoru await velkou pozornost. Pozastaví GetUrlContentLengthAsyncse:

  • GetUrlContentLengthAsync nemůže pokračovat, dokud getStringTask nebude dokončen.
  • Mezitím se ovládací prvek vrátí volajícímu GetUrlContentLengthAsync.
  • Jakmile bude dokončený, ovládací prvek se tady getStringTask obnoví.
  • Operátor await pak načte string výsledek z getStringTask.

Příkaz return určuje celočíselnou výsledek. Všechny metody čekající na GetUrlContentLengthAsync načtení hodnoty délky.

Pokud GetUrlContentLengthAsync nemá žádnou práci, kterou může provést mezi voláním GetStringAsync a čekáním na dokončení, můžete kód zjednodušit voláním a čekáním v následujícím jediném příkazu.

string contents = await client.GetStringAsync("https://learn.microsoft.com/dotnet");

Následující charakteristiky shrnují, co dělá z předchozího příkladu asynchronní metodu:

  • Podpis metody obsahuje async modifikátor.

  • Název asynchronní metody končí podle konvence příponou „Async“.

  • Návratový typ je jeden z následujících typů:

    • Task<TResult> pokud vaše metoda má návratový příkaz, ve kterém operand má typ TResult.
    • Task pokud vaše metoda nemá žádný návratový příkaz nebo má návratový příkaz bez operandu.
    • void pokud píšete asynchronní obslužnou rutinu události.
    • Jakýkoli jiný typ, který má metodu GetAwaiter .

    Další informace najdete v části Návratové typy a parametry .

  • Metoda obvykle obsahuje alespoň jeden await výraz, který označuje bod, kdy metoda nemůže pokračovat, dokud není dokončena očekávaná asynchronní operace. Během této doby je metoda pozastavena a ovládací prvek se vrátí volajícímu metody. Další část tohoto tématu ukazuje, co se stane v okamžiku pozastavení.

V asynchronních metodách používáte zadaná klíčová slova a typy pro označení, jakou akci chcete provést, a kompilátor udělá zbytek, včetně udržování přehledu o tom, co musí nastat, když se řízení vrátí do bodu „await“ pozastavené metody. Některé běžné procesy, jako je zpracování smyček a výjimek, může být v tradičním asynchronním kódu obtížné zpracovat. V asynchronní metodě zapisujete tyto prvky podobně, jako byste to udělali v synchronním řešení, a problém je vyřešen.

Další informace o asynchronii v předchozích verzích rozhraní .NET Framework naleznete v tématu TPL a tradiční asynchronní programování rozhraní .NET Framework.

Co se stane v asynchronní metodě

Nejdůležitějším principem, který je třeba pochopit v asynchronním programování, je, jak ovládat přesuny toků od metody k metodě. Následující diagram vás provede procesem:

Trace navigation of async control flow

Čísla v diagramu odpovídají následujícím krokům zahájeným při volání metody volání asynchronní metody.

  1. Volání metody volá a čeká asynchronní metodu GetUrlContentLengthAsync .

  2. GetUrlContentLengthAsyncHttpClient vytvoří instanci a zavolá asynchronní metodu GetStringAsync pro stažení obsahu webu jako řetězec.

  3. Něco se stane v GetStringAsync tom, že pozastaví jeho průběh. Možná je třeba vyčkat na dokončení stahování nebo jiné blokující aktivity na webu. Aby se zabránilo blokování prostředků, GetStringAsync dává řízení volajícímu , GetUrlContentLengthAsync.

    GetStringAsync vrátí hodnotu Task<TResult>, kde TResult je řetězec a GetUrlContentLengthAsync přiřadí úkol proměnné getStringTask . Úkol představuje probíhající proces volání GetStringAsync, se závazkem vytvořit skutečnou řetězcovou hodnotu po dokončení práce.

  4. Vzhledem k tomu getStringTask , že ještě nebyl očekáván, GetUrlContentLengthAsync může pokračovat v další práci, která nezávisí na konečném výsledku z GetStringAsync. Tato práce je reprezentována voláním synchronní metody DoIndependentWork.

  5. DoIndependentWork je synchronní metoda, která provádí svou práci a vrací se do volajícího.

  6. GetUrlContentLengthAsync má nedostatek práce, že to může dělat bez výsledku z getStringTask. GetUrlContentLengthAsync Dále chce vypočítat a vrátit délku staženého řetězce, ale metoda nemůže tuto hodnotu vypočítat, dokud metoda nebude mít řetězec.

    GetUrlContentLengthAsync Proto pomocí operátoru await pozastaví svůj průběh a získá kontrolu nad metodou, která volala GetUrlContentLengthAsync. GetUrlContentLengthAsync vrátí volajícímu hodnotu Task<int> . Úloha představuje slib vyrábět celé číslo výsledku, který má délku staženého řetězce.

    Poznámka:

    Pokud GetStringAsync se (a proto getStringTask) dokončí před tím, než GetUrlContentLengthAsync ho čeká, zůstane ovládací prvek v GetUrlContentLengthAsync. Náklady na pozastavení a následné vrácení GetUrlContentLengthAsync by byly plýtvání, pokud se volaný asynchronní proces getStringTask již dokončil a GetUrlContentLengthAsync nemusí čekat na konečný výsledek.

    Uvnitř volající metody pokračuje vzor zpracování. Volající může udělat jinou práci, která nezávisí na výsledku před GetUrlContentLengthAsync čekáním na tento výsledek, nebo volající může okamžitě čekat. Volající metoda čeká na GetUrlContentLengthAsynca GetUrlContentLengthAsync čeká na GetStringAsync.

  7. GetStringAsync dokončí a vytvoří výsledek řetězce. Výsledek řetězce není vrácen voláním GetStringAsync tak, jak byste mohli očekávat. (Nezapomeňte, že metoda již vrátila úlohu v kroku 3.) Místo toho je výsledek řetězce uložen v úloze, která představuje dokončení metody, getStringTask. Operátor await načte výsledek z getStringTask. Příkaz přiřazení přiřadí načtený výsledek contents.

  8. Pokud GetUrlContentLengthAsync má výsledek řetězce, metoda může vypočítat délku řetězce. GetUrlContentLengthAsync Práce je také dokončena a obslužná rutina čekající události může pokračovat. V úplném příkladu na konci tématu si můžete potvrdit, že obslužná rutina události načte a vytiskne hodnotu výsledné délky. Pokud jste v oblasti asynchronního programování nováčky, zvažte rozdíl mezi synchronním a asynchronním chováním. Synchronní metoda je vrácena, jakmile je její práce dokončena (krok 5), ale asynchronní metoda vrátí hodnotu úlohy, když je její práce pozastavena (kroky 3 a 6). Když asynchronní metoda nakonec dokončí svou práci, je úloha označena jako dokončená a výsledek, pokud existuje, je uložen v úloze.

Asynchronní metody rozhraní API

Možná vás zajímá, kde najít metody, jako GetStringAsync je například podpora asynchronního programování. .NET Framework 4.5 nebo novější a .NET Core obsahují mnoho členů, se async kterými pracují a await. Můžete je rozpoznat podle přípony "Async", která je připojena k názvu člena, a jejich návratovým Task typem nebo Task<TResult>. Třída například obsahuje metody, System.IO.Stream jako CopyToAsyncje , ReadAsynca WriteAsync vedle synchronních metod CopyTo, Reada Write.

Prostředí Windows Runtime obsahuje také mnoho metod, které můžete používat s aplikacemi async pro Windows a await v nich. Další informace najdete v tématu Vytváření vláken a asynchronní programování pro vývoj pro UPW a asynchronní programování (aplikace pro Windows Store) a rychlý start: Volání asynchronních rozhraní API v jazyce C# nebo Visual Basic, pokud používáte starší verze prostředí Windows Runtime.

Vlákna

Asynchronní metody mají být neblokující operace. Výraz await v asynchronní metodě neblokuje aktuální vlákno, zatímco je spuštěna očekávaná úloha. Namísto toho se výraz zaregistruje pro zbývající metody jako pokračování a vrátí řízení volajícímu asynchronní metody.

await Klíčová async slova a klíčová slova nezpůsobí vytvoření dalších vláken. Asynchronní metody nevyžadují multithreading, protože asynchronní metoda není spuštěna ve vlastním vlákně. Metoda pracuje na aktuálním kontextu synchronizace a používá čas ve vlákně pouze v případě, že je metoda aktivní. Můžete použít Task.Run k přesunutí práce vázané na procesor na vlákno na pozadí, ale vlákno na pozadí nepomůže s procesem, který jen čeká na zpřístupnění výsledků.

Asynchronní přístup při asynchronním programování se doporučuje v téměř každém případě existujících přístupů. Konkrétně je tento přístup lepší než BackgroundWorker třída pro vstupně-výstupní operace, protože kód je jednodušší a nemusíte se chránit před podmínkami časování. V kombinaci s metodou Task.Run je asynchronní programování lepší než BackgroundWorker u operací vázaných na procesor, protože asynchronní programování odděluje podrobnosti koordinace spouštění kódu od práce, která Task.Run přenese do fondu vláken.

async a await

Pokud určíte, že metoda je asynchronní metoda pomocí modifikátoru async , povolíte následující dvě funkce.

  • Označená asynchronní metoda může použít funkci await k určení bodů pozastavení. Operátor await říká kompilátoru, že asynchronní metoda nemůže pokračovat až do dokončení očekávaného asynchronního procesu. Během této doby se ovládací prvek vrátí volajícímu asynchronní metody.

    Pozastavení asynchronní metody ve výrazu await nepředstavuje konec metody a finally bloky se nespustí.

  • Samotná označená asynchronní metoda může být očekávána metodami, které ji volaly.

Asynchronní metoda obvykle obsahuje jeden nebo více výskytů await operátoru, ale absence await výrazů nezpůsobí chybu kompilátoru. Pokud asynchronní metoda nepoužívá await operátor k označení bodu pozastavení, metoda se provede jako synchronní metoda i přes async modifikátor. Kompilátor u takových metod zahlásí upozornění.

async a await jsou kontextová klíčová slova. Další informace a příklady naleznete v následujících tématech:

Návratové typy a parametry

Asynchronní metoda obvykle vrací Task nebo Task<TResult>. Uvnitř asynchronní metody await se operátor použije na úlohu vrácenou z volání jiné asynchronní metody.

Jako návratový typ zadáte Task<TResult> , pokud metoda obsahuje return příkaz, který určuje operand typu TResult.

Jako návratový typ se používá Task , pokud metoda nemá žádný návratový příkaz nebo má návratový příkaz, který nevrací operand.

Můžete také zadat jakýkoli jiný návratový typ za předpokladu, že typ obsahuje metodu GetAwaiter . ValueTask<TResult> je příkladem takového typu. Je k dispozici v balíčku NuGet System.Threading.Tasks.Extension .

Následující příklad ukazuje, jak deklarujete a voláte metodu, která vrací Task<TResult> nebo a Task:

async Task<int> GetTaskOfTResultAsync()
{
    int hours = 0;
    await Task.Delay(0);

    return hours;
}


Task<int> returnedTaskTResult = GetTaskOfTResultAsync();
int intResult = await returnedTaskTResult;
// Single line
// int intResult = await GetTaskOfTResultAsync();

async Task GetTaskAsync()
{
    await Task.Delay(0);
    // No return statement needed
}

Task returnedTask = GetTaskAsync();
await returnedTask;
// Single line
await GetTaskAsync();

Každá vrácená úloha představuje probíhající práci. Úloha zapouzdřuje informace o stavu asynchronního procesu a posléze buď konečný výsledek z procesu, nebo výjimku, kterou proces vyvolá v případě neúspěchu.

Asynchronní metoda může mít také návratový void typ. Tento návratový typ se používá především k definování obslužných rutin událostí, kde je vyžadován návratový void typ. Asynchronní obslužné rutiny událostí často slouží jako výchozí bod pro asynchronní programy.

Asynchronní metoda, která má návratový void typ, nelze očekávat a volající metody void-returning nemůže zachytit žádné výjimky, které metoda vyvolá.

Asynchronní metoda nemůže deklarovat parametry ref nebo out, ale metoda může volat metody, které mají tyto parametry. Podobně asynchronní metoda nemůže vrátit hodnotu podle odkazu, i když může volat metody s návratovými hodnotami ref.

Další informace a příklady najdete v tématu Asynchronní návratové typy (C#).

Asynchronní rozhraní API v prostředí Windows Runtime programování mají jeden z následujících návratových typů, které se podobají úlohám:

Konvence pojmenování

Podle konvence by metody, které vracejí běžně očekávané typy (například Task, Task<T>, ValueTask, ValueTask<T>) měly mít názvy, které končí na "Async". Metody, které spouští asynchronní operaci, ale nevrací očekávaný typ, by neměly mít názvy, které končí na "Async", ale mohou začínat na "Begin", "Start" nebo nějaký jiný příkaz navrhnout tuto metodu nevrací nebo vyvolá výsledek operace.

Můžete ignorovat konvenci, kde událost, základní třída a rozhraní smlouvy navrhují odlišný název. Například byste neměli přejmenovat běžné obslužné rutiny událostí, například OnButtonClick.

Související články (Visual Studio)

Titulek Popis
Paralelní provádění více webových požadavků pomocí asynchronního volání a operátoru await (C#) Ukazuje, jak spustit několik úloh současně.
Asynchronní návratové typy (C#) Znázorňuje typy, které asynchronní metody mohou vrátit, a vysvětluje, kdy je každý typ vhodný.
Zrušení úloh pomocí tokenu zrušení jako signalizačního mechanismu Ukazuje, jak přidat k asynchronnímu řešení následující funkce:

- Zrušení seznamu úkolů (C#)
- Zrušení úkolů po určité době (C#)
- Zpracování asynchronní úlohy při jejich dokončení (C#)
Použití async pro přístup k souborům (C#) Seznam a ukázka výhod použití operátorů async a await při přístupu k souborům.
Asynchronní vzor založený na úlohách (TAP) Popisuje asynchronní vzor, vzor je založený na typech a Task<TResult> typechTask.
Asynchronní videa na channel 9 Poskytuje odkazy na různá videa o asynchronním programování.

Viz také