Metoda For ()
În TPL, paralelismul datelor este suportat, în special, folosind metoda For () definită în clasa Parallel. Această metodă există în mai multe forme. Vom începe să ne gândim la cea mai simplă formă de mai jos:
where fromInclusive înseamnă valoarea inițială a ceea ce corespunde variabilei de control a buclă; se mai numește și valoarea iterativă sau index; o valoare exclusivă, o valoare mai mare decât valoarea finală. La fiecare etapă a ciclului, variabila de control a buclă este incrementată cu una. Prin urmare, ciclul trece treptat de la valoarea inițială de la Inclusiv la valoarea finală la Exclusiv minus unul.
Codul executabil ciclic este indicat de metoda trecută prin parametrul corporal. Această metodă trebuie să fie compatibilă cu delegatul de acțiune
Pentru metoda For (), parametrul generalizat T trebuie, desigur, să fie de tip int. Valoarea trecută prin parametrul obj va fi următoarea valoare a variabilei de control a buclă. Și metoda trecută prin parametrul corporal poate fi numită sau anonimă. Metoda For () returnează o instanță a unui obiect de tip ParallelLoopResult, care descrie starea finală a buclă. Pentru cicluri simple, această valoare poate fi neglijată.
Caracteristica principală a metodei For () este că permite, atunci când există această posibilitate, paralelizarea execuției codului într-o buclă. Și acest lucru, la rândul său, poate duce la creșterea productivității. De exemplu, procesul de transformare a unei matrice într-o buclă poate fi împărțit în părți astfel încât diferite părți ale matricei să fie convertite simultan. Trebuie avut în vedere, totuși, că îmbunătățirile de performanță nu sunt garantate datorită diferențelor în ceea ce privește numărul de procesoare disponibile în diferite medii de execuție și deoarece paralelizarea ciclurilor mici poate aduce costuri care depășesc timpul salvat.
În exemplul de mai jos, programul demonstrează folosirea metodei For () în practică. La începutul acestui program, se creează o serie de date care constau în valori întregi de 1.000.000.000. Apoi este apelată metoda For (), iar metoda MyTransform () este trecută ca "corpul" bucla. Această metodă constă dintr-o serie de operatori care realizează transformări arbitrare în matricea de date. Scopul său este de a simula o operațiune specifică. După cum se va explica în detaliu mai jos, operațiunea ar trebui să fie netrivială, astfel încât paralelismul datelor să aibă un efect pozitiv. În caz contrar, executarea secvențială a bucla poate dura mai repede:
Acest program este format din două cicluri. În primul, standard, pentru buclă, matricea de date este inițializată. Și în a doua bucla, executată în paralel cu metoda For (), se face o transformare pe fiecare element al matricei de date. După cum sa menționat mai sus, această transformare este arbitrară și aleasă numai pentru demonstrații. Metoda For () împarte automat apelurile din metoda MyTransform () în părți pentru prelucrarea paralelă a fragmentelor individuale de date stocate în matrice. Prin urmare, dacă executați acest program pe un computer cu două sau mai multe procesoare disponibile, atunci ciclul de conversie a datelor în matrice poate fi efectuat utilizând metoda For () în paralel.
Ar trebui totuși să se țină seama de faptul că nu toate ciclurile pot fi realizate eficient când sunt paralelizate. De obicei, buclele mici, precum și buclele care constau în operații foarte simple, se realizează mai rapid într-o manieră secvențială decât cele paralele. De aceea, pentru inițializarea matricei de date nu este paralelizată metoda For () din programul considerat aici.
Paralelizarea buclelor mici și foarte simple poate fi ineficientă, deoarece timpul necesar pentru organizarea sarcinilor paralele, precum și timpul petrecut pentru comutarea în context, depășesc timpul economisit prin concurrency. În sprijinul acestui fapt, următorul program exemplu creează o buclă serial și paralelă, iar pentru comparație, timpul de execuție al fiecăruia dintre ele este afișat:
Mai întâi de toate, acordați atenție faptului că versiunea paralelă a ciclului de inițializare a matricei de date este de aproximativ două ori mai lent decât cea de serie. Faptul este că, în acest caz, atât de puțin timp este cheltuit pentru operațiunea de atribuire, încât costurile pentru paralelizarea organizată suplimentară depășesc economiile pe care le oferă.
Rețineți, în plus, că versiunea paralelă a ciclului de conversie a datelor este mai rapidă decât cea de serie. În acest caz, economiile rezultate din paralelizare pot compensa mai mult costurile pentru organizarea suplimentară a acestora.
În mod tipic, în ceea ce privește beneficiile paralelizării diferitelor tipuri de cicluri, trebuie să respectați recomandările Microsoft actuale. În plus, trebuie să vă asigurați că paralelizarea bucla duce într-adevăr la îmbunătățiri de performanță înainte de a utiliza un astfel de ciclu în codul de aplicație lansat în cele din urmă.
În ceea ce privește programul de mai sus, este necesar să menționăm alte două caracteristici ale acestuia. Mai întâi, observați că într-o buclă paralelă este folosită o expresie lambda pentru a inițializa datele. Aici, "corpul" ciclului este indicat în expresia lambda. (Amintiți-vă că este creată o metodă anonimă în expresia lambda). Prin urmare, pentru execuția paralelă prin metoda For (), nu este necesară specificarea unei metode numite.
În al doilea rând, observați utilizarea clasei Cronometru pentru a calcula timpul de execuție al buclei. Această clasă se află în spațiul de nume System.Diagnostics. Pentru a-l utiliza, e suficient să creați o instanță a obiectului său și apoi să apelați metoda Start (), care începe raportul de timp, apoi metoda Stop (), care finalizează numărătoarea inversă. Și cu ajutorul metodei Reset (), numărul de timp este resetat la starea inițială.
Timpul de execuție poate fi obținut în mai multe moduri. În acest program, proprietatea Elapsed este folosită în acest scop. care returnează un obiect TimeSpan. Cu acest obiect și proprietatea TotalSeconds, timpul este afișat în câteva secunde, inclusiv fracții de secundă. Cum exemplul programului considerat aici demonstrează, clasa Stopwatch este foarte utilă atunci când se elaborează un cod executabil concurent.
După cum sa menționat mai sus, metoda For () returnează o instanță a unui obiect de tip ParallelLoopResult. Aceasta este o structură în care sunt definite următoarele două proprietăți:
Proprietatea IsCompleted va avea o valoare booleană a adevăratului dacă toate pașii din buclă sunt executați. Cu alte cuvinte, dacă buclele se termină în mod normal, această proprietate va conține o valoare booleană a valorii adevărate. Dacă execuția bucla este întreruptă înainte de timp, atunci această proprietate va conține valoarea logică false. Proprietatea LowestBreakIteration va conține cea mai mică valoare a variabilei control buclă dacă bucla este întreruptă în prealabil apelând metoda ParallelLoopState.Break ().
Pentru a accesa un obiect de tip ParallelLoopState, utilizați forma metodei For (), al cărui delegat acceptă starea curentă a buclă ca al doilea parametru. Sub această formă a metodei For () este dată forma cea mai simplă:
În această formă, delegatul acțiunii care descrie corpul bucla este definit după cum urmează:
Pentru metoda For (), parametrul generalizat T1 trebuie să fie de tip int, iar parametrul generalizat T2 este de tip ParallelLoopState. Ori de câte ori se numește delegatul de acțiune, starea curentă a buclă este trecută ca arg2.
Pentru a termina bucla prematur, utilizați metoda Break (). Un apel la o instanță a unui obiect de tip ParallelLoopState în interiorul corpului bucla, definit de parametrul corpului. Metoda Break () este declarată după cum urmează:
Apelarea metodei Break () generează o cerere pentru o terminare anterioară a ciclului concurent, care poate apărea după mai mulți pași de buclă după apelarea metodei Break (). Dar toți pașii din buclă înainte de a apela metoda Break () sunt încă executați. De asemenea, trebuie avut în vedere faptul că anumite părți ale ciclului nu pot fi executate în paralel. Deci, dacă se execută 10 pași ai buclă, acest lucru nu înseamnă că toți cei 10 pași reprezintă primele 10 valori ale variabilei de control a buclă.
Întreruperea unei buclă care este rulată în paralel cu metoda For () este adesea utilă atunci când căutați date. Deci, dacă se găsește valoarea dorită, atunci nu este nevoie să continuați ciclul. Întreruperea ciclului poate fi utilă în cazul în care în timpul următoarei operațiuni s-au întâlnit date nesigure.
Metoda ForEach ()
unde sursa denotă o colecție de date procesate într-o buclă, iar corpul este o metodă care va fi executată la fiecare etapă a buclei. După cum am explicat mai devreme, toate rețelele, colecțiile și alte surse de date suportă interfața IEnumerabilă
Metoda Similar Pentru (), metoda de execuție paralelă foreach () bucla poate fi oprită prin apelarea () metoda Break de tip instanță ParallelLoopState obiectului transmis prin corpul parametru, cu condiția ca metoda folosită formularul foreach () de mai jos: