V prostředí ASP.NET Web Forms od verze .NET 4.5 je možné použít asynchronní metody typu async void například v metodě Page_Load. Přestože by bylo logičtější použít Task, který by ASP.NET mohl sledovat a počkat na jeho dokončení před renderováním stránky, z důvodu zpětné kompatibility zůstává signatura void. Framework si však přesto udržuje kontrolu nad životním cyklem asynchronních operací pomocí speciálního SynchronizationContext, který sleduje jejich stav a zajišťuje, že se další fáze zpracování stránky spustí až po jejich dokončení.
Tento přístup ale přináší rizika. Synchronizační kontext v ASP.NET Web Forms je jednovláknový. To znamená, že pokud během asynchronního zpracování dojde k blokování vláken pomocí .Result nebo .Wait(), může dojít k deadlocku – hlubší await výrazy nebudou schopny se obnovit, protože vlákno bude obsazené. Vždy je proto nutné se vyhnout synchronnímu čekání na výsledek asynchronních metod a místo toho pracovat s await napřímo.
S nástupem Windows Runtime (WinRT), zejména v aplikacích pro Windows 8 a ARM zařízení, bylo asynchronní programování povýšeno na jednu z hlavních architektonických zásad. WinRT definuje své API pomocí společného formátu metadat WinMD, který umožňuje jazykovým projekcím – tedy aby .NET, JavaScript i C++ mohly se stejným rozhraním pracovat přirozeným způsobem. Ačkoli většina knihoven WinRT je napsána v nativním kódu, je možné psát vlastní komponenty i v C# – ovšem pouze s využitím kompatibilních typů.
Na rozdíl od .NET, kde se pracuje s typy Task a Task<T>, WinRT používá rozhraní IAsyncAction a IAsyncOperation<T>, které plní podobnou funkci. Tyto rozhraní nejsou součástí .NET, ale díky projekci mohou být v C# používány, jako by jimi byla. Klíčovým aspektem je, že .NET poskytuje rozšiřující metody, které zajišťují kompatibilitu s await. Například metoda RetrieveFeedAsync z SyndicationClient vrací IAsyncOperation<SyndicationFeed>, ale lze ji v C# použít běžným způsobem:
Chce-li vývojář získat instanci Task z IAsyncOperation, může použít metodu AsTask(), která umožní další manipulaci – například použití v kombinátorech typu Task.WhenAll, nebo nastavení ConfigureAwait(false):
Zvláštní přístup zvolil WinRT i v oblasti zrušení asynchronních operací. Místo předávání CancellationToken jako parametru je zrušení integrováno přímo do vráceného objektu. Každý IAsyncOperation nebo IAsyncAction má metodu Cancel(), kterou lze vyvolat. Přesto je doporučeno využívat rozšíření AsTask(cancellationToken), které zajišťuje propojení s běžnou .NET infrastrukturou pro správu zrušení:
Sledování průběhu asynchronní operace je ve WinRT rovněž vestavěnou vlastností. Pro tyto účely existují specializovaná rozhraní IAsyncActionWithProgress<TProgress> a IAsyncOperationWithProgress<TResult, TProgress>, která umožňují sledování změn průběhu operace prostřednictvím události. Opět se doporučuje využít přetíženou verzi AsTask, která umožní předat standardní .NET IProgress<T>:
Pokud je třeba využít současně jak sledování zrušení, tak průběhu, existuje přetížená verze, která přijímá oba parametry najednou.
Při psaní vlastních WinRT komponent v C# je nutné zajistit, aby veřejné rozhraní komponenty používalo pouze typy kompatibilní s WinMD – tedy žádné .NET specifické typy jako Task. K tomu opět slouží rozšiřující metody AsAsyncOperation() a AsAsyncAction(), které převádějí Task na odpovídající WinRT rozhraní:
Tento přístup umožňuje využívat asynchronní programování v prostředí WinRT naplno, bez ztráty kompatibility s jinými technologiemi nebo jazyky. Vzhledem k tomu, že typy AsTask a AsAsyncOperation si navzájem rozumí, je práce mezi .NET a WinRT překvapivě plynulá.
Vývojář by měl také brát v úvahu specifika synchronizačního kontextu v prostředí, kde kód běží. Například v UI vláknech nebo v ASP.NET Web Forms je nutné se vyhýbat synchronnímu čekání na asynchronní metody. Správné využití ConfigureAwait(false) je zásadní tam, kde na návratovou hodnotu nečekáme v kontextu UI, čímž předejdeme potenciálním deadlockům a zvýšíme výkon.
Proč je asynchronní programování nezbytné a jak funguje v jazyce C#?
V tradičním blokujícím kódu je pořadí vykonávání jednoduché a předvídatelné: operace proběhne, až skončí, pokračuje další příkaz. V asynchronním programování to však neplatí – další řádek kódu často běží dříve, než se dokončí dlouhotrvající operace na pozadí. Proto bylo nutné vyvinout různé vzory, jak reagovat na dokončení asynchronní úlohy: kód se může vložit přímo do operace, přihlásit k události dokončení, nebo předat tzv. callback – delegáta či lambda výraz, který se spustí po skončení. Pokud má následná akce proběhnout na specifickém vlákně (například UI vlákno v aplikaci WinForms nebo WPF), je třeba navíc zajistit správné zařazení do fronty toho vlákna. Vše je tím pádem složité a ne příliš elegantní.
Výhodou asynchronního kódu je uvolnění vlákna, na kterém byla operace spuštěna. Vlákna totiž představují náročný systémový zdroj a čím méně jich program spotřebuje, tím lépe. Hlavně však v uživatelských rozhraních existuje často pouze jedno vlákno schopné přijímat a zpracovávat uživatelské vstupy a vykreslovat okna. Pokud toto vlákno zablokujeme, aplikace přestane reagovat a uživatel získá dojem, že je „zamrzlá“. To je důvod, proč je u desktopových UI aplikací asynchronita klíčová.
V jazyce C# ve verzi 5.0 přinesl Microsoft revoluční řešení této problematiky prostřednictvím nových klíčových slov async a await, která umožňují psát asynchronní kód, jež na první pohled vypadá velmi podobně jako ten blokující. Tento mechanismus není jen knihovna – kompilátor program přetvoří podobně jako u lambda výrazů nebo iterátorů, přičemž eliminuje potřebu komplikovaných vzorů a callbacků.
Například metoda, která stahuje webovou stránku blokujícím způsobem, vypadá následovně:
Ekvivalentní asynchronní metoda je:
Na první pohled téměř nerozeznatelné, avšak pod povrchem se děje zásadní proměna. Klíčové je použití await, které označuje místo, kde se metoda „rozbije“ a pokračování se přesune do zvláštní metody. Metoda DownloadStringTaskAsync spustí stahování na pozadí, hlavní vlákno metodu ukončí a může se věnovat jiným úkolům, například vykreslování UI nebo zpracování uživatelských vstupů. Jakmile stahování skončí, zavolá se zpětná metoda, která naváže tam, kde byl await.
Tento přístup však neznamená, že asynchronní programování je bez nástrah. Chování při výjimkách, práce s návratovými hodnotami, správné zacházení s kontexty vláken a vliv na výkon jsou věci, které vyžadují hlubší porozumění. Jinak mohou nastat nečekané chyby, které je těžké diagnostikovat.
U desktopových aplikací je asynchronní kód zásadní zejména pro zachování dojmu plynulého a responzivního uživatelského rozhraní. UI vlákno nesmí být blokováno, protože i krátká prodleva nad pár desítek milisekund je uživatel schopen vnímat jako zpomalení či zaseknutí programu. Proto i v nejběžnějších UI frameworkách (WinForms, WPF, Silverlight) existuje jen jedno vlákno pro UI, aby bylo možné jednoduše synchronizovat stav a předcházet závodním podmínkám.
Asynchronní kód umožňuje UI vláknu vrátit se k zpracování uživatelských událostí nebo animací a díky tomu aplikace zůstává interaktivní i během dlouhotrvajících operací, jako je síťová komunikace nebo výpočty na pozadí. Navíc async otevírá prostor pro využití paralelního zpracování na jemnější úrovni, což může vést k výrazným optimalizacím a novým architekturám programů.
Pochopení toho, jak async a await skutečně fungují na úrovni vláken a jak s nimi zacházet, je nezbytné pro správné využití této technologie. Programátor musí být ostražitý, jak pracuje s kontextem synchronizace, protože špatné zacházení může vést k mrtvým zámkům, nečekaným výjimkám či poklesu výkonu. Také je důležité si uvědomit, že async neznamená automatické paralelní spuštění – je to spíše elegantní způsob, jak efektivně spravovat čekání na dlouhotrvající operace bez zablokování vláken.

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский