Budoucnost asynchronního programování v C#


Asynchronní programování

Asynchronní programování se od synchronního liší tím, že při volání asynchronní metody není blokováno hlavní vlákno po celou dobu, kdy probíhá nějaká časově náročná operace. Metoda tedy ”skončí” ještě předtím než je znám výsledek celé operace a v okamžiku, kdy je operace dokončena, je volána callback metoda.

Tento přístup je dobře známý z komunikace s webovými službami, které umožňují volat metody synchronně i asynchronně (je nutné povolit při přidávání reference, v Silverlightu pouze asynchronní metody). Callback metody jsou zde volány tak, že se delegát na tuto metodu přidává události [WebMethodName]Completed – tento delegát je zavolán v okamžiku, kdy je operace dokončena a umožňuje přístup k výsledku operace.

Výhody asynchronního programování:

  • Reagující UI – UI vlákno je okamžitě volné a reaguje na vstup od uživatele
  • Škálovatelnost serveru – vlákno není blokované jedním požadavkem a může být využito pro zpracování jiných požadavků

Současné možnosti

Asynchronní metody je samozřejmě možné psát i v současné době. Následující příklad ukazuje, jak přepsat synchronní metodu (na delší dobu zablokuje hlavní vlákno) na asynchronní (hlavní vlákno blokovat nebude). Příklad sice nemá praktický význam, ale ilustruje vše důležité.

Synchronní metoda:

private void DownloadButton_Click(object sender, RoutedEventArgs e)
{
    //Volání synchronní metody, která bude blokovat hlavní vlákno
    //a operace nebude reagovat na uživatele
    var result = this.GetStringFromUrl(this.UrlTextBox.Text);
    this.ShowResult(result);
}

//Synchronní metoda, která stahuje obsah zadané url jako řetězec
private string GetStringFromUrl(string url)
{
    var webClient = new WebClient();

    //Časově náročná operace, která blokuje hlavní vlákno
    var result = webClient.DownloadString(new Uri(url));

    //Metoda končí po dokončení operace, 
    //ve chvíli, kdy je dostupný výsledek a je možné ho vrátit
    return result;
}

Asynchronní metoda, která provede to samé:

private void DownloadButton_Click(object sender, RoutedEventArgs e)
{
    //Volání asynchronní metody, která nebude blokovat hlavní vlákno
    //a operace bude reagovat na uživatele - výsledek je zobrazen v callbacku
    this.GetStringFromUrlAsync
        (
            this.UrlTextBox.Text,
        //Anonymní metoda jako callback metoda
            (result) =>
            {
                this.ShowResult(result);
            }
        );
}

//Asynchronní metoda, která stahuje obsah zadané url jako řetězec 
//a vrací ho při volání callback metody
private void GetStringFromUrlAsync(string url, Action callback)
{
    var webClient = new WebClient();

    //Předávání anonymního metody jako callback metody
    //pro asynchronní stahování řetězce
    webClient.DownloadStringCompleted += (sender, ea) =>
        {
            //Výsledek je dostupný až v callback metodě
            var result = ea.Result;
            if (callback != null)
            {
                callback(result);
            }
        };
    //Časově náročná operace, která je volána asynchronně
    webClient.DownloadStringAsync(new Uri(url));

    //Metoda končí v okamžiku, kdy je zavolána asynchronní operace, 
    //tedy okamžitě a hlavní vlákno je volné
}

Myslím, že i na tomto úplně základním příkladu je zcela zřejmé, jak se snížila například čitelnost kódu. A to je opravdu základní věc, kdy nemáme žádné zvláštní požadavky a nepotřebujeme v metodě GetStringFromUrlAsync provádět s výsledkem nějaké další operace (například zpracování výsledku). V tu chvíli by se kód ještě znepřehlednil a byla by větší šance udělat někde nějakou chybu.

I tvůrci C# si zřejmě mysleli, že psaní asynchronních metod není tak elegantní, jak by se na tak důležitou věc slušelo. Proto do nové verze C# (C# 5.0) připravili novinku týkající se asynchronního programování.

Budoucnost asynchronního programování v C# – novinka v C# 5.0

Hlavní změny jsou v přidání dvou klíčových slov do jazyku C#:

Modifikátor async:

  • určuje, že metoda (nebo lambda výraz) je asynchronní – ne že je automaticky zpracovávaná asynchronně, ale že obsahuje volání asynchronních metod pomocí operátorů await (viz dále)
  • taková metoda je zpracovávána v hlavním vlákně tak dlouho, jak jen to je možné

Operátor await:

  • vrací kontrolu, dokud není úloha označená operátorem await (čekající úloha) dokončena – neznamená to tedy čekání na zpracování (blokování hlavního vlákna – synchronní operace), ale že když takováto úloha ještě neskončila, je zbytek metody označen jako pokračování této úlohy a to je vyvoláno po skončení této úlohy
  • určuje, že od tohoto místa může být metoda zpracovávána v jiném než hlavním vlákně
  • prakticky nahrazuje psaní pokračujících úloh, což umožňuje TPL – pokračování tedy vytváří kompilátor a ne programátor

Pro zde popisovaný příklad vypadá asynchronní metoda (a její volání) s pomocí nové asynchronní funkcionality:

//Asynchronní metoda, která neblokuje hlavní vlákno a UI
//může stále reagovat na požadavky uživatele
private async void DownloadButton_Click(object sender, RoutedEventArgs e)
{
    //Volání asynchronní metody
    var result = await this.GetStringFromUrlAsync(this.UrlTextBox.Text);
    this.ShowResult(result);
}

//Asynchronní metoda, která stahuje obsah zadané url jako řetězec 
//a vrací úlohu, která provádí operaci a vrací požadovaný výsledek
private async Task GetStringFromUrlAsync(string url)
{      
    var webClient = new WebClient();

    //Volání asynchronní metody (označena modifikátorem async),
    //která je zpracovávána v jiném vlákně a neblokuje hlavní vlákno
    var result = await webClient.DownloadStringTaskAsync(new Uri(url));
    //Tato část je kompilátorem označena jak pokračování úlohy označené
    //klíčovým slovem await a je volána po dokončení této úlohy

    return result;
}

Z příkladu je zcela zřejmé, že s touto novou funkcionalitou je přepsání synchronní metody na asynchronní více než jednoduché a hlavně přímočaré a elegantní! Stačí přidat na správná místa klíčová slova async a await.

Čitelnost takového kódu také zůstává dobrá a prakticky není nic, kde by se dala udělat chyba. Veškerý kód okolo vytváří až kompilátor.

Dále si možná někdo všimne toho, že ve třídě WebClient přibyla metoda DownloadStringTaskAsync, která odpovídá právě nové, zde popisované funkcionalitě. Toto napovídá, že v podobném duchu budou přepsány (nebo přidány) všechny asynchronní metody v .NET frameworku a budou vracet Task<T>. Takže asynchronní metody ve frameworku budou odpovídat asynchronním metodám, které budou psát programátoři.

Stejně tak je v příkladu vidět použití třídy Task<T>. Je to návratový typ async metody, která má vracet nějaký výsledek. V příkladu jsou vidět obě možnosti pro async metody – návratový typ void a návratový typ Task<T>.

Závěr

V tomto článku byla představena novinka připravovaná do budoucí verze C#, která si určitě zaslouží pozornost. A protože tento názor sdílí i tvůrci C#, tak tomuto tématu věnovali již docela dost prostoru – viz odkazy. Navíc Microsoft poskytnul vývojářům k dispozici prototyp (CTP) k vyzkoušení již teď – opět viz odkazy.

Odkazy:

Dokument Asynchrony in .NET

, , , , , ,

Komentáře jsou uzavřeny.