MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Clasa Parallel

Clasa Parallel face parte din TPL și este concepută pentru a simplifica execuția paralelă a codului. Parallel are o serie de metode care permit paralelizarea unei sarcini.

Una dintre metodele care permit execuția paralelă a sarcinilor este metoda Invoke:

// metoda Parallel.Invoke execută trei metode
Parallel.Invoke(
   Print,
   () =>
   {
       Console.WriteLine($"Se execută sarcina {Task.CurrentId}");
       Thread.Sleep(3000);
   },
   () => Square(5)
);

void Print()
{
   Console.WriteLine($"Se execută sarcina {Task.CurrentId}");
   Thread.Sleep(3000);
}

// calculăm pătratul unui număr
void Square(int n)
{
   Console.WriteLine($"Se execută sarcina {Task.CurrentId}");
   Thread.Sleep(3000);
   Console.WriteLine($"Rezultatul {n * n}");
}

Metoda Parallel.Invoke primește ca parametru un array de obiecte Action, ceea ce înseamnă că putem transmite în această metodă un set de metode care vor fi apelate la execuția sa. Numărul de metode poate varia, dar în acest caz, am definit trei metode. La fel ca în cazul clasei Task, putem transmite fie numele metodei, fie o expresie lambda.

Astfel, dacă mașina țintă are mai multe nuclee, aceste metode vor fi executate în paralel pe diferite nuclee. Ieșire pe consolă a programului:

Se execută sarcina 1
Se execută sarcina 3
Se execută sarcina 2
Rezultatul 25

Metoda Parallel.For

Metoda Parallel.For permite execuția paralelă a iterațiilor unui ciclu. Are următoarea definiție:

For(int, int, Action<int>)

Primul parametru al metodei specifică indexul de început al elementului din ciclu, iar al doilea parametru - indexul final. Al treilea parametru - delegatul Action - specifică metoda care va fi executată o dată pe iterație:

Parallel.For(1, 5, Square);

// calculăm pătratul unui număr
void Square(int n)
{
   Console.WriteLine($"Se execută sarcina {Task.CurrentId}");
   Console.WriteLine($"Pătratul numărului {n} este {n * n}");
   Thread.Sleep(2000);
}

În acest caz, primul parametru transmis metodei Parallel.For este numărul 1, iar al doilea - numărul 5. Astfel, metoda va itera de la 1 la 4 inclusiv. Al treilea parametru reprezintă metoda care calculează pătratul unui număr. Deoarece acest parametru reprezintă tipul Action<int>, metoda trebuie să accepte un parametru de tip int.

Parametrul transmis metodei este contorul care parcurge ciclul de la 1 la 4 inclusiv. Metoda Square va fi astfel apelată de 4 ori. Ieșire pe consolă:

Se execută sarcina 1
Se execută sarcina 2
Pătratul numărului 4 este 16
Se execută sarcina 4
Pătratul numărului 1 este 1
Se execută sarcina 3
Pătratul numărului 3 este 9
Pătratul numărului 2 este 4

Metoda Parallel.ForEach

Metoda Parallel.ForEach realizează iterații pe o colecție care implementează interfața IEnumerable, similar ciclului foreach, dar execută iterațiile în paralel. Are următoarea definiție:

ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source,

Action<TSource> body)

Primul parametru reprezintă colecția care va fi iterată, iar al doilea parametru - delegatul care se execută o dată pe iterație pentru fiecare element al colecției.

La ieșire, metoda returnează structura ParallelLoopResult, care conține informații despre execuția ciclului.

ParallelLoopResult result = Parallel.ForEach<int>(
   new List<int>() { 1, 3, 5, 8 },
   Square
);

// calculăm pătratul unui număr
void Square(int n)
{
   Console.WriteLine($"Se execută sarcina {Task.CurrentId}");
   Console.WriteLine($"Pătratul numărului {n} este {n * n}");
   Thread.Sleep(2000);
}

În acest caz, folosim o colecție de obiecte int, astfel că metoda transmisă ca al doilea parametru trebuie să accepte un parametru de tip int. Ieșire pe consolă:

Se execută sarcina 1
Se execută sarcina 3
Pătratul numărului 8 este 64
Se execută sarcina 4
Pătratul numărului 3 este 9
Se execută sarcina 2
Pătratul numărului 5 este 25
Pătratul numărului 1 este 1

Ieșirea din ciclu

În ciclurile for și foreach standard, ieșirea anticipată din ciclu se face cu ajutorul operatorului break. În metodele Parallel.ForEach și Parallel.For, putem ieși din ciclu înainte de finalizarea acestuia:

ParallelLoopResult result = Parallel.For(1, 10, Square);
if (!result.IsCompleted)
   Console.WriteLine($"Execuția ciclului s-a terminat la iterația {result.LowestBreakIteration}");

// calculăm pătratul unui număr
void Square(int n, ParallelLoopState pls)
{
   if (n == 5) pls.Break();    // dacă parametrul este 5, ieșim din ciclu

   Console.WriteLine($"Pătratul numărului {n} este {n * n}");
   Thread.Sleep(2000);
}

Aici, metoda Square, care procesează fiecare iterație, acceptă un parametru suplimentar - obiectul ParallelLoopState. Dacă contorul din ciclu ajunge la valoarea 5, se apelează metoda Break. Acest lucru face ca sistemul să iasă din execuția metodei Parallel.For la prima ocazie disponibilă.

Metodele Parallel.ForEach și Parallel.For returnează un obiect ParallelLoopResult, care are două proprietăți importante:

  • IsCompleted: indică dacă execuția completă a ciclului paralel s-a finalizat
  • LowestBreakIteration: returnează indexul la care execuția ciclului a fost întreruptă

Deoarece în exemplul nostru execuția ciclului se întrerupe la indexul 5, proprietatea IsCompleted va avea valoarea false, iar LowestBreakIteration va fi egală cu 5.

← Lecția anterioară Lecția următoare →