Novine koje dolaze sa C# 5.0 već su postale „lanjski snijeg“, obzirom da je već prošlo 4 mjeseca od pdc10 na kojem je Anders Hejlsberg prezentirao buduću verziju C# 5.0. Pored kompajler kao servis, i još nekoliko novina, prezentiran je novi koncept programiranja nazvan asinhrono programiranje. Šta je asinhrono programiranje i zašto nam može poslužiti, biće u prezentirano nekoliko blog postova.
Uvod
Kako možemo definisati asinhrono programiranje?
To nije ništa drugo do jedna vrsta višenitnog programiranja, ali bez direktnog formiranja niti.
Kako bi na najjednostavniji način pokušali objasniti asinhrono programiranje potrebno je prvo definisati i detektovati probleme u razvoju aplikacija do kojih je dovela pojava ove vrste programiranja.
Višenitno programiranje zadnjih godina postaje sve više prisutno, jer aplikacije koje se danas razvijaju postaju više ovisne od raznih servisa koji se obično nalaze na udaljenim računarima ili pak u oblacima. S druge strane, postoje aplikacije koje u svom sastavu imaju funkcije koje izvršavaju vrlo duge operacije, pri čemu je korisnički interfejs uglavnom nepomičan i zamrznut, te danas predstavlja staromodan i prevaziđen način dizajniranja aplikacija.
Podsjećanja radi, svaka .NET (slobodno možemo reći i bilo koja Windows) aplikacija posjeduje samo jednu nit i ona se zove UI ili glavna nit (main thread). Bez dodatnog programiranja u dotičnoj aplikaciji sve operacije se izvršavaju sekvencijalno: jedna po jedna odnosno, svaka operacija mora čekati sve prethodne operacije da se izvrše da bi se sama izvršila. Što u kontektu prethodnih razmatranja predstavlja vrlo ozbiljan problem, jer mi želimo da imamo mogućnost izvršavanja, paralelno više operacija odjednom, jer imamo hardver koji to podržava, imamo memoriju koja to može prihvatiti, i na kraju imamo više procesorskih jezgri koje to mogu obraditi. S druge strane zahtjevi kupaca za takvim aplikacijama postalo je must. Pa zašto bi čekali, jer na kraju vrijeme je novac.
Kako .NET danas može doskočiti ovim problemima?
Prvenstveno tako što će „long-running“ operacije „prebaciti“ iz UI niti u radnu nit (worker thread), tako da ova prva bude slobodna i da može reagovati na korisničke aktivnosti.
A kako se ovakve logike implementiraju?
Višenitnim programiranjem koje je u principu složeno i za najiskusnije developere, a posebno onda kad ih je puno i potrebno pronaći neki logički bug, koji se tu pojavio nakon svih mogućih testova. Tad dotični programer kune majku što ga je rodila.
Od samog pojavljivanja .NET višenitno programiranje zastupljeno je u određenoj mjeri i od prve verzije pokušava se ovaj način programiranja usavršiti, što u principu predstavlja i zadaću .NET frameworka. Prije samog upoznavanja sa novim ključnim riječima async i await, vrlo je važno upoznati se sa današnjim tehnikama višenitnog programiranja koje će ovdje kratno biti prezentirane.
Današnje tehnike za višenitno programiranje
Klasa Thread
Formirati radnu nit i u njoj pozvati funkciju da nešto izvrši, jednostano možemo uraditi koristeći klasu Thread, koja se nalazi u System.Threading. Primjer kako se to radi prikazano je na sljedćem listingu.
class Program { static void Main(string[] args) { // thwo worker thread is constructed Thread thread1 = new Thread(new ThreadStart(Operation1)); Thread thread2 = new Thread(new ThreadStart(Operation2)); // first start thread 1 thread1.Start(); // then start thread two thread2.Start(); //Press any key to continue Console.ReadKey(); } public static void Operation1() { System.Threading.Thread.Sleep(2000); Console.WriteLine("I am operation one."); } public static void Operation2() { System.Threading.Thread.Sleep(1000); Console.WriteLine("I am operation two."); } }
Rezultat gornjeg listinga je prikazan na sljedećoj slici.
Sa slike možemo vidjeti da se prva izvršila metoda koja se druga startala tj. Operation2. Po konceptu sekvencijalnog programiranja ovo je nemoguće, ali u koliko smo formirali dvije potpuno neovisne niti, koje se izvršavaju neovisno od glavne UI niti, ovo je potpuno normalno. Oko višenitnog programiranja možete više saznati na linku koji se nalazi u referencama za ovaj blog post.
BackgroundWorker novina u .NET 2.0
Sirovo korištenje Thread klase predstavljalo je problem, jer na mnogim dijelovima u aplikaciji potrebno je formirati samo jednu radni nit, u koju se treba poslati neka operacija za izvršavanje. Novina koja je došla sa .NET 2.0 zvala se BackgrounWorker i predstavljala je jednu od najpopularnijih komponenata u WinForms toolboxu. Formiranje jedne radne niti preko BackgroundWorker komponente postalo je dosta pojednostavljeno. Na sljedećoj slici prikazan je jedan od načina kako definisati ovu komponentu u aplikaciji. Obzirom da se nalazila kao komponenta u Toolboxu, drag & drop funkcijom moguće je formirati u aplikaciji.
Nakon povlačenja i spuštanja u formu (br. 1), potrebno je definisati događaje: DoWork 1, ProgressChanged 2 i ProgressCompleted 3, te implementirati logiku. Naredni listing prikazuje kako radi BackgroundWorker.
Pored definisanja događaja potrebno je backgroundWorker podesiti da obavještava o progresu, na način kako je prikazano na slici.
Kada kliknemo na dugme Operation1, program starta sa procesuiranjem, i nakon svakog intervala preko ProgressChanged informacija o progresu se proslijedi, te na kraju procesuiranja pozove ProgressCompleated informirajući da se operacija zavrsila. Važno je napomenuti da su ovi događaji pozivaju u UI niti, i slobodno možemo „dirati“ kontrole Form1.
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //Call DoWork in workre thread backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i < 100; i++) { Thread.Sleep(500); //Reports progress about operation state backgroundWorker1.ReportProgress(i+1); } } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { progressBar1.Value = e.ProgressPercentage; } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { MessageBox.Show("Operation1 has been completed."); } }
BackgroundWorker je pravo jednostavan, ali pri složenijoj logici ipak moramo pribjeći formiranju objekata Thread klase, te se upustiti u pustolovinu višenitnog programiranja.
Zaključak
Prethodno su prikazane najčešće tehnike za višenitno programiranje te stoga i dovoljna uvertira u analizu novih ključnih riječi u C# 5.0 i višenitnog odnosno asinhronog programiranja.
U sljedećem postu upustićemo se u analizu async i await ključnih riječi te vidjeti kako programirati višenitno bez niti, ili u prevodu kako sekvencijalan kod natjerati da radi višenitno, te pokušati prognozirati da li je stvarno došao kraj višenitnom programiranju kao jednom od najsloženijih tehnika programiranja.
Izvorni kod za prvi dio članka o asinhronom programiranju možete skinuti sa ovog linka. Ažurirana verzija izvornog koda može se pronaći na ovom linku.
Pronašao sam ovaj Vaš blog post i pokušao ga implementirati, međutim izbaciva mi exception koji možete vidjeti na ovom linku: http://img696.imageshack.us/i/img056rs.jpg/
Hvala…
Blog post je ažuriran i dodana je jedna slika koja rješava tvoj problem. Na kraju posta dodan novi demo koji je proširen sa dugmetom Cancel i otklonjena greška.
Pingback: Asinhrono programiranje nova proširenja C# 5.0 i narednoj verziji Visual Studia - Blog o C++ i C#
Pingback: Ho to convert your old sequential code in to async « Bahrudin Hrnjica Blog
Pingback: How to convert your old sequential code in to async - C# & .NET technologies - developers.de