Čuda asinkronog programiranja 2.dio

thumb image

U prošlom članku sam se osvrnuo na to kako izbjeći deadlock pri asinkronom programiranju sa async/await ključnim riječima.
U ovom članku ću više obratiti pozornost na to kako iskoristiti async/await ključne riječi, onako kako bi se trebali iskorištavati.

Za početak, valja spomenuti kako je najvažnija stvar u cijelo priči Task klasa, koja nam omogućava cijelu magiju transformacije sinkronih metoda u asinkroni tip. Za one koje ne znaju Task klasa je stigla sa .NET verzijom 4.0, ali neizbježnu upotrebu je doživjela u .NET verziji 4.5 (odnosno 4.5.1) gdje je i predstavljen C# 5.0 koji je od najboljih noviteta uveo upravo async/await ključne riječi.

Što zapravo znači asinkrono programiranje?

Asinkrono programiranje nije ništa drugo nego oslobađanje glavnog programskog threada (ili dretve ako više volite naš jezik u programerskom svijetu). Točnije, zamislite si da imate klasičnu Windows Forms aplikaciju, te na njoj imate hrpu kontrola poput: TextBox, Button,…

Sve te kontrole su po default-u vezane za glavni programski thread i koliko god se trudili, izvršavanje tih kontrola će se uvijek izvršavati na glavnom threadu. I sada si zamislite da imate metodu (funkciju) koja izvršava spajanje na vašu bazu i vrši nekakav masivni upit nad nekom tablicom. Naravno, programu treba neko vrijeme da izvrši spajanje (pogotovo ako vam se baza nalazi u cloud-u), izvrši sav taj query, zatvori vezu prema bazi, te vam vrati podatke koje vraća taj upit. Ponekada se to zna izvršavati i po nekoliko sekundi (ili ako imate jako spori internet, onda se zna izvršavati i po nekoliko minuta). Dok se to izvršava (sinkrono), ostatku programa (pa i kontrolama) nećete moći pristupiti, jer se metoda za rad s bazom izvršava. Tek kada se ta metoda izvrši (i u najboljem slučaju se više niti jedna metoda iza te ne izvršava), vi ćete moči raditi s ostatkom programa. Takav način rada vam zna prikazati jako iritantnu poruku “Not responding” u vrhu programa. Upravo zato postoji asinkroni način programiranja, koji će vašu sinkronu metodu pretvoriti u asinkronu metodu na način da će u određenom trenutku (kada pozovete ključnu riječ await) stvoriti novi thread na kojem će se vaša metoda izvršiti, kako ne bi gušili glavni thread.

Oprez: nemojte misliti kako će vaša metoda odmah postati asinkrona, odnosno da će se stvoriti zaseban thread čim pozovete asinkronu metodu, jer će se na drugom threadu izvršavati samo onaj dio koji se nalazi pod ključnom riječi await.

Naravno, vi se možete držati stare škole i sve raditi putem interface-a IAsyncResult, ali to je bespotrebno stvaranje viška koda i čitljivost i razumljivost koda je odmah slabija. Postoje razni tutoriali na internetu koji vas uče kako raditi sa async/await ključnim riječima, ali većina vas uči samo kako raditi sa ugrađenim asinkronim metodama koje dolaze uz .NET, što vama dosta često neće ništa značiti. Jedan od korisnih primjera je znati kako dobiti html kod sa neke stranice putem ugrađene asinkrone metode:

WebClient client = new WebClient();
client.DownloadStringAsync(new Uri("www.bing.com"));

Ali kako vi svoju vlastitu metodu možete pretvoriti u asinkronu metodu? Pa, princip je toliko jednostavan da ne može biti jednostavniji.
Stil asinkronog programiranja koji ću vam sada pokazati se zove TAP (Task Async Pattern). Prije nego vam pokažem sami princip, važno je znati sljedeća pravila:

  1. nakon pristupnog modifikatora slijedi ključna riječ async
  2. asinkrona metoda uvijek ima povratni tip Task<T>, gdje je T tip podataka koju metoda vraća
  3. parametri asinkrone metode moraju biti isti kao i kod sinkrone metode
  4. ako ne koristite konzolnu aplikaciju, morat ćete iskoristiti metodu CofigureAwait (koju sam objasnio u prethodnom članku)
  5. vaša asinkrona metoda vraća Task u kojem će se nalaziti vrijednost koju je zapravo vratila metoda (vrijednost dohvaćate putem Result property-a)

Ovako ide princip:

private bool RadSaBazom()
{
     //kod za rad sa bazom
     return true;
}

public async Task<bool> RadSaBazomAsync()
{
     return await Task.Run(() => RadSaBazom());
}

public void MetodaIzKojeSeDobivaRezultatAsinkroneMetode()
{
     bool istina = RadSaBazomAsync().Result;   //preko svojstva Result se dobiva vrijednost koju vrati asinkrona metoda
}

Da bi znali točno kako se kod odvija prilikom asinkronog izvršavanja možete pogledati sljedeću sliku:

Asinkroni model

Ili možete postaviti breakpoint u kodu i vidjeti detaljnije što se događa u pozadini izvršavanja.

VAŽNO: Nemojte bezveze stvarati novi thread, jer novi thread zauzima od prilike 1 MB (po default-u) u Thread Pool-u (MSDN) (opet,biti ćete limitirani sa brojem threadova koje možete koristiti)

Pin It