Sdílet prostřednictvím


Model asynchronního programování úloh

Můžete se vyhnout kritickým bodům výkonu a zvýšit celkovou odezvu aplikace pomocí asynchronního programování. Tradiční techniky pro psaní asynchronních aplikací ale můžou být složité, takže je obtížné psát, 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 dělá obtížnou práci, kterou vývojář použil, a vaše aplikace uchovává logickou strukturu, která se podobá synchronnímu kódu. V důsledku toho získáte všechny výhody asynchronního programování s zlomkem úsilí.

Toto téma obsahuje přehled o tom, kdy a jak používat asynchronní programování a obsahuje odkazy na témata podpory, která obsahují podrobnosti 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. V asynchronním procesu může aplikace pokračovat v jiné práci, která nezávisí na webovém prostředku, dokud se potenciálně blokující úloha nedokončí.

Následující tabulka uvádí typické oblasti, ve kterých asynchronní programování zlepšuje rychlost odezvy. 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 Typy prostředí Windows Runtime s asynchronními metodami
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ě cenná pro aplikace, které přistupují k uživatelskému rozhraní (UI), protože veškerá činnost spojená s UI obvykle sdílí jedno vlákno. Pokud je nějaký proces v synchronní aplikaci blokovaný, jsou všechny blokované. Vaše aplikace přestane reagovat a může dojít k závěru, že selhala, když místo toho čeká.

Když používáte asynchronní metody, aplikace bude dál reagovat na uživatelské rozhraní. Velikost okna můžete změnit nebo minimalizovat, nebo můžete aplikaci zavřít, pokud nechcete čekat na dokončení.

Asynchronní přístup přidá ekvivalent automatického přenosu do seznamu možností, ze které si můžete vybrat při návrhu asynchronních operací. To znamená, že získáte všechny výhody tradičního asynchronního programování, ale s mnohem menším úsilím od 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 vám mělo být povědomé.

Kompletní příklad Windows Presentation Foundation (WPF) je k dispozici ke stažení z asynchronního programování s async a await 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 signaturou 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 stringmožnost GetStringAsync .

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

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

Příkaz return určuje celočíselný výsledek. Jakékoli metody, které čekají na GetUrlContentLengthAsync, načtou hodnotu 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 podle konvence končí příponou "Async".

  • Návratový typ je jedním 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 pro 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. Mezitím se metoda pozastaví 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 pomocí zadaných klíčových slov a typů označíte, co chcete udělat, a kompilátor provede zbytek, včetně sledování toho, co se musí stát, když se ovládací prvek vrátí k bodu await v pozastavené metodě. Některé rutinní procesy, jako jsou smyčky a zpracování výjimek, mohou být obtížné zpracovat v tradičním asynchronním kódu. V asynchronní metodě napíšete tyto prvky podobně jako v synchronním řešení a problém se vyřeší.

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ší věcí, kterou je potřeba pochopit v asynchronním programování, je způsob, jakým se tok řízení přesouvá od metody k metodě. Následující diagram vás provede procesem:

Trasování asynchronního řízení toku

Čísla v diagramu se vztahují k následujícím krokům, které začínají, když volající metoda volá asynchronní metodu.

  1. Metoda volající zavolá a čeká na asynchronní metodu GetUrlContentLengthAsync.

  2. GetUrlContentLengthAsync vytvoří instanci HttpClient a spustí asynchronní metodu GetStringAsync pro stažení obsahu webové stránky jako řetězce.

  3. Něco se stane v GetStringAsync, co pozastaví jeho průběh. Možná musí počkat na stažení webu nebo na nějakou jinou blokující aktivitu. 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. Protože getStringTask ještě nebylo čekáno, 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í svému volajícímu.

  6. GetUrlContentLengthAsync došla práce, kterou může dělat bez výsledku od 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.

    Proto GetUrlContentLengthAsync používá operátor await k pozastavení svého průběhu a předání kontroly metodě, která volala GetUrlContentLengthAsync. GetUrlContentLengthAsync vrátí volajícímu hodnotu Task<int> . Úkol představuje příslib k vytvoření celočíselného výsledku, který je délkou staženého řetězce.

    Poznámka:

    Pokud se GetStringAsync (a tedy getStringTask) dokončí dříve, než na něj GetUrlContentLengthAsync čeká, zůstává řízení 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 vzorec zpracování pokračuje. Volající může udělat jinou práci, která nezávisí na výsledku, předtím než vyčká na tento výsledek, nebo volající může neprodleně vyčkat. Volající metoda čeká na GetUrlContentLengthAsynca GetUrlContentLengthAsync čeká na GetStringAsync.

  7. GetStringAsync dokončuje a produkuje řetězcový výsledek. 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řazuje načtený výsledek do 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 můžete potvrdit, že obslužná rutina události načte a vytiskne hodnotu výsledku délky. Pokud s asynchronním programováním začínáte, zvažte rozdíl mezi synchronním a asynchronním chováním chvíli. Synchronní metoda vrátí, když je její práce dokončena (krok 5), ale asynchronní metoda vrátí hodnotu úkolu, když je jeho práce pozastavena (kroky 3 a 6). Když asynchronní metoda nakonec dokončí svou práci, úkol se označí jako dokončený a výsledek , pokud existuje, je uložen v úkolu.

Asynchronní metody rozhraní API

Možná vás zajímá, kde najít metody, jako je GetStringAsync, které podporují asynchronní programování. .NET Framework 4.5 nebo novější a .NET Core obsahují mnoho členů, které pracují s async 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, jako je System.IO.Stream, CopyToAsync, ReadAsync a WriteAsync spolu se synchronními metodami CopyTo, Read a Write.

Modul Windows Runtime obsahuje také mnoho metod, které můžete používat s async a await v aplikacích pro Windows. 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. Místo toho výraz zaregistruje zbytek metody jako pokračování a vrátí řízení volajícímu asynchronní metody.

async a await klíčová slova nevedou k vytvoření dalších vláken. Asynchronní metody nevyžadují multithreading, protože asynchronní metoda neběží ve vlastním vlákně. Metoda běží v aktuálním kontextu synchronizace a používá čas ve vlákně pouze v době, kdy 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 k asynchronnímu programování je vhodnější než existující přístupy téměř v každém případě. Konkrétně je tento přístup lepší než BackgroundWorker třída pro operace závislé na vstupu/výstupu, protože kód je jednodušší a nemusíte se obávat konkurentních podmínek. 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. Do té 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í.

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

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 vydá upozornění pro tyto metody.

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

Návratové typy a parametry

Asynchronní metoda obvykle vrací Task nebo Task<TResult>. Uvnitř asynchronní metody se operátor await použije na úlohu, která je vrácena 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ý úkol představuje probíhající práci. Úloha zapouzdřuje informace o stavu asynchronního procesu a nakonec buď konečný výsledek procesu, nebo výjimku, kterou proces vyvolá, pokud se to nepodaří.

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 in, ref nebo out, ale může volat metody, které takové parametry mají. 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 programování prostředí Windows Runtime mají jeden z následujících návratových typů, které jsou podobné ú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.

Konvenci můžete ignorovat, pokud událost, základní třída nebo kontrakt rozhraní navrhne jiný 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 úkolů najednou.
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 do asynchronního řešení přidat následující funkce:

- Zrušení seznamu úkolů (C#)
- Zrušení úkolů po určité době (C#)
- Zpracování asynchronního úkolu při jeho dokončení (C#)
Použití async pro přístup k souborům (C#) Uvádí a demonstruje výhody použití 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 Task a Task<TResult>.
Asynchronní videa na channel 9 Obsahuje odkazy na různá videa o asynchronním programování.

Viz také