Sistemele de operare moderne suporta multitasking (capacitatea de a incarca mai multe programe in memorie simultan si sa imparta timpul CPU intre programe). De exemplu, momentul cand in sistemul de operare Windows am deschis mai multe programe (Winamp, Internet Explorer, Messenger).
Sistemele de operare moderne suporta multithreading. (capacitatea de a suporta mai multe unitati de executie intr-un singur proces, fiecare thread(fir de executie) avand “bucati” din timpul CPU).
Am ales sa scriu un articol despre threading pentru ca orice aplicatie, cat de cat serioasa, nu poate rula pe un singur thread (nevoia de a rula alte operatii in acelasi timp in background este mare). Problema intervine atunci cand este nevoie ca ele sa imparta aceleasi resurse.
.Net Compact Framework suporta Threading prin doua moduri:
- obiectul Thread (spawn, inchidere, transmitere de mesaje intre thread-uri)
- Timer
System.Threading.Thread incapsuleaza toata functionalitea oferita de thread in Windows Mobile din Compact Framework.
Vom crea o mini-aplicatie multithread care va rula un proces in background, dar care va permite utilizatorului sa interactioneze in continuare prin controale.
Cand e bine sa pornim un thread?
Daca programul e pe cale sa angreneze un proces despre care se stie ca va dura si pentru ca utilizatorul sa nu astepte, atunci este bine sa pornim thread-ul care va lucra la procesare, lasand thread-ul principal al UI sa ruleze.
De exemplu, in aplicatia noastra pe care o vom dezvolta, vom avea o metoda care va realiza un calcul ce va necesita un timp mai mare de executie.
Adaugam spatiul de nume necesar:
using System.Threading;
Pentru a crea si a porni un thread in .Net Compact Framework, trebuie sa urmam pasii:
- Cream o instanta a System.Threading.ThreadStart. Transmitem ca parametru numele metodei care va fi executat in thread, metoda care trebuie sa fie de tip void si sa nu accepte parametri
//PAS1 instanta ThreadStart ThreadStart firstThreadStarg = new ThreadStart(Count);
- Cream o instanta a System.Threading.Thread. Transmitem ThreadStart in constructorul Thread si astfel vom avea o referinta catre thread-ul care va executa metoda specificata in Pasul 1.
//PAS2 instanta THREAD Thread firsThread = new Thread(firstThreadStart);
- Apelam metoda Start(), astfel codul din metoda referinta va fi executat in cadrul thread-ului respectiv. Acesta nu va porni pana la apelul metodei Start.
//PAS3 start thread firsThread.Start();
Metoda Count:
private void Count() { for(int i=0; i<100000;i++) { result = +i; //EXCEPTIE this.lblInfo.Text = result.ToString(); } }
In .Net Compact Framework, controale nu pot fi “folosite” de firele de executie care nu detin propriu-zis controlul( nu putem porni un fir de executie si apoi sa actualizam elemente din interfata utilizator din acel tread prin interactionarea directa cu controale. Daca vom face acest lucru, vom primi exceptia:
In .Net Framework exista Form.BeginInvoke si Form.EndInvoke, metode care nu sunt In .Net Compact Framework. Solutia consta in crearea unui delegate pentru o metoda care va face schimbarile in interfata utilizator. Apoi, folosim Form.Invoke pentru apelul delegatului din thread.
private void Count() { for(int i=0; i<100000;i++) { result = +i; this.Invoke(new EventHandler(UpdateControl)); } } private void UpdateControl(object sender, EventArgs e) { lblInfo.Text = result.ToString(); }
Inchiderea unei aplicatii multithreading
O aplicatie nu este considerata oprita pana cand toate firele de executie sunt oprite. Daca un thread se afla intr-un ciclu si aplicatia se inchide, el poate face ca procesul sa nu se termine.
O solutie este sa suprascriem metoda Form.OnClosing(). Astfel, fiecare thread poate sa determine daca aplicatia incearca sa se inchida. In cazul in care acesta determina ca este ultimul fir, aplicatia se poate inchide.
Suspendarea unui thread
Pentru a suspenda executia unui thread, se foloseste metoda Thread.Sleep(), in acest mod blocandu-se intreg codul executat la apelarea thread-ului. Ca parametru va primi un numar de milisecunde, timp in care thread-ul va “dormi” inainte sa fie asezat in coada de asteptare a procesorului.
// va suspenda codul din thread-ul curent // 10000 millisecunde Thread.Sleep(10000);
Stim ca o instanta a clasei Timer este foarte utila atunci cand vrem sa executam cod la anumite intervale. Clasa Timer este alegerea perfecta atunci cand avem nevoie sa executam operatii la intervale regulate de timp, dar operatii nu de mare complexitate.
Avantajele clasei Timer asupra clasei Thread in folosirea firelor de executie sunt controlul usor al executarii codului la un anumit interval, ”sleep” implicit atunci cand nu se executa codul plus faptul ca in interfata utilizator controalele pot fi accesate de codul executat de clasa Timer.
In Compact Framework, exista limitari in ceea ce priveste notiunuea de Threading:
- numarul maxim de procese este 32.
- fiecare process primeste maxim memorie 32 MB.
- cu toate ca thread-urile sunt mai putin costisitoare, crearea prea multor fire de executie poate afecta performanta.
Suportul pentru Threading in .Net Compact Framewokr permite dezvoltarea unor aplicatii smart-device mult mai sofisticate prin mutarea in background a proceselor care necesita mai mult timp pentru procesare. Aceasta decizie necesita o intelegere foarte buna a implicatiilor si a optiunilor existente la acel moment. Daca este folosita corect, procesarea in background imbunatateste timpul de raspuns al aplicatiei si furnizeaza o experienta superioara pentru utilizator.