Čuda asinkronog programiranja 1.dio

thumb image

Nedavno sam radio sa Windows Forms aplikacijom koja je morala imati mogućnost služenja sa MDF bazom podataka (Service base/lokalna ako vam je tako lakše). Super! Kreirao sam novi projekt, dodao bazu (.mdf) i koristio svojeg najboljeg prijatelja, .NET 4.5 (C# 5.0) koji ima moćnu stvar, a to je lakše korištenje asinkronog programiranja uz ključne riječi async i await.

Sjećate li se rada s IAsyncResult interface-om za callback-ove? Kada se samo sjetim kako je to izgledalo nasprem async/await koda, pozli mi. Prije koji mjesec sam kupio O’Reilly knjigu “Async in C# 5.0″ autora Alex Davies, što je izvrsna knjiga i možete je nabaviti na O’Reilly stranici. Knjiga je fantastična i za početnike i za napredne.

U toj sam knjizi pronašao TAP (Task Async Pattern) pristup za pisanje asinkronih metoda s async/await ključnim riječima, što je najjednostavniji i najbolji pristup za rješavanje asinkronih metoda s ključnim riječima async/await. Postoji nekoliko malih pravila kojih se trebate pridržavati kako bi vaša sinkrona metoda radila kao asinkrona.

Vratimo se Windows Forms aplikaciji. Kreirao sam nekoliko tablica, stvorio relacije i ostale stvari koje su mi bile potrebne za osnovni rad sa bazom. Za rad s bazom sam koristio DataSet klasu, jer nisam osoba koja voli pisati dugačke kodove koji mogu zadati glavobolju s razno raznim exceptionima. Ta klasa je super i zanimljiva, ali neke stvari se moraju pisati ručno jer DataSet nije predviđen za takve stvari.

Stvorio sam zatim klasu koja predstavlja rad sa svim tablicama u bazi, stvorivši tako metode za rad s CRUD operacijama. Ja, naravno, ne bih bio ja da ne pretvorim te sinkrone metode (koje mogu biti jako teške za aplikaciju) u asinkrone. Zamislite da imate petlju kroz koju ćete barem 50 puta izvršavati dotični upit… možete zaboraviti na nekoliko trenutaka rad s ostatkom dijela aplikacije jer će vam se gušiti glavna dretva (main/ui thread) aplikacije. Asinkrona metoda će u jednom trenutku (ne objašnjavam ovaj dio u ovom članku) za nas stvoriti dretvu na kojoj će se ona izvršavati kako bi nesmetano mogli dalje raditi s aplikacijom, a da nam se ista ne zamrzne (i ne stvori poruku “Not responding”). S obzirom da sam stekao naviku koristiti TAP pristup, tako sam i ovaj put pristupio asinkronom programiranju. Ovako izgleda primjer TAP-a:

public async Task<T> MethodAsync()
{
     //naravno stavite parametre metode, ako ih imate
     return await Task.Run(()=>Method());
}

Da malo pojasnim samu metodu. Stavljamo public pristupni modifikator (jer u ovom slučaju ne želimo koristiti sinkronu verziju metode koja je u mojem slučaju private), iza toga slijedi ključna riječ async koja naznačuje da je riječ o asinkronoj metodi. Za povratni tip podataka koristimo Task<T>, gdje je T jedan od tipova podataka (jednostavnih ili složenih; ovo je jedan od sitnih pravila asinkronog programiranja s async/await). Za slučaj da metoda ne vraća ništa onda umjesto Task<T> koristimo void povratni tip te ne koristimo return u našem kodu.

Da bi preuzeli rezultat naše asinkrone metode, koristimo se na sljedeći način:

var result = MyMethodAsync().Result;

Ili ako je void metoda u pitanju, onda samo pozovemo metodu.

E sada, kada bismo radili sa konzolnim aplikacijama, ovo bi sve bilo super i radilo bi. No, u ostalim aplikacijama dogodilo bi se nešto što se zove deadlock. Ukratko, deadlock je gubljenje konteksta iz asinkrone metode. Tada se smrzava aplikacija pa u njoj ne možete raditi apsolutno ništa. Kako bi izbjegli čupanje kose, primijenite asinkroni kod na ovaj način:

public async Task<T> MethodAsync()
{
     //naravno stavite parametre metode, ako ih imate
     return await Task.Run(()=>Method().ConfigureAwait(continueOnCapturedContext: false););
}

ConfigureAwait metoda će u kratko modificirati await na način da mu odredite true ili false za parametar continueOnCapturedContext, što u našem slučaju treba biti false kako bi izbjegli deadlock.

 

Članak preveden sa autorskog bloga: http://csharponline.blog.com/

Pin It