MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Execuție secvențială și paralelă - Task.WhenAll și Task.WhenAny

O metodă asincronă poate conține multe expresii await. Când sistemul întâlnește operatorul await într-un bloc de cod, execuția în metoda asincronă se oprește până când sarcina asincronă se finalizează. După finalizarea sarcinii, controlul trece la următorul operator await și așa mai departe. Acest lucru permite apelarea sarcinilor asincrone în mod secvențial, într-o anumită ordine. De exemplu:

await PrintAsync("Hello C#");
await PrintAsync("Hello World");
await PrintAsync("Hello FDC.COM");

async Task PrintAsync(string message)
{
   await Task.Delay(2000);     // imitația unei operații de lungă durată
   Console.WriteLine(message);
}

Afișarea pe consolă a acestui program:

Hello C#
Hello World
Hello FDC.COM

Adică, vedem că apelurile PrintAsync se execută secvențial în ordinea în care sunt definite în cod. Fiecare sarcină se execută cel puțin 2 secunde, astfel timpul total de execuție al celor trei sarcini va fi de cel puțin 6 secunde. Și în acest caz, afișarea este strict deterministă.

Deseori, această secvență este necesară dacă o sarcină depinde de rezultatele altei sarcini.

Totuși, acest lucru nu este întotdeauna necesar. În astfel de cazuri, putem lansa toate sarcinile în paralel și aplica operatorul await acolo unde este necesar să garantăm finalizarea unei sarcini, de exemplu, la sfârșitul programului.

// definim și lansăm sarcinile
var task1 = PrintAsync("Hello C#");
var task2 = PrintAsync("Hello World");
var task3 = PrintAsync("Hello FDC.COM");

// așteptăm finalizarea sarcinilor
await task1;
await task2;
await task3;

async Task PrintAsync(string message)
{
   await Task.Delay(2000);     // imitația unei operații de lungă durată
   Console.WriteLine(message);
}

În acest caz, toate sarcinile se lansează și se execută în paralel, astfel timpul total de execuție va fi mai mic de 6 secunde, iar afișarea pe consolă a programului este nedeterministă. De exemplu, poate fi următoarea:

Hello FDC.COM
Hello C#
Hello World

Totuși, .NET permite simplificarea monitorizării execuției unui set de sarcini cu ajutorul metodei Task.WhenAll. Această metodă primește un set de sarcini asincrone și așteaptă finalizarea tuturor acestor sarcini. Aceasta este echivalentul metodei statice Task.WaitAll(), dar destinată în mod special metodelor asincrone și permite aplicarea operatorului await:

// definim și lansăm sarcinile
var task1 = PrintAsync("Hello C#");
var task2 = PrintAsync("Hello World");
var task3 = PrintAsync("Hello FDC.COM");

// așteptăm finalizarea tuturor sarcinilor
await Task.WhenAll(task1, task2, task3);

async Task PrintAsync(string message)
{
   await Task.Delay(2000);     // imitația unei operații de lungă durată
   Console.WriteLine(message);
}

Începutul programului lansează trei sarcini. Apoi Task.WhenAll creează o nouă sarcină, care va fi executată automat după finalizarea tuturor sarcinilor furnizate, adică sarcinile task1, task2, task3. Și cu ajutorul operatorului await așteptăm finalizarea acesteia.

Dacă trebuie să așteptăm finalizarea a cel puțin unei sarcini dintr-un set de sarcini, putem folosi metoda Task.WhenAny(). Aceasta este echivalentul metodei Task.WaitAny() - se finalizează când se finalizează cel puțin o sarcină. Dar pentru a aștepta finalizarea, la Task.WhenAny() se aplică operatorul await:

// definim și lansăm sarcinile
var task1 = PrintAsync("Hello C#");
var task2 = PrintAsync("Hello World");
var task3 = PrintAsync("Hello FDC.COM");

// așteptăm finalizarea a cel puțin unei sarcini
await Task.WhenAny(task1, task2, task3);

async Task PrintAsync(string message)
{
   await Task.Delay(new Random().Next(1000, 2000));     // imitația unei operații de lungă durată
   Console.WriteLine(message);
}

Obținerea rezultatului

Sarcinile transmise la Task.WhenAll și Task.WhenAny pot returna o anumită valoare. În acest caz, din metodele Task.WhenAll și Task.WhenAny putem obține un array care va conține rezultatele sarcinilor:

// definim și lansăm sarcinile
var task1 = SquareAsync(4);
var task2 = SquareAsync(5);
var task3 = SquareAsync(6);

// așteptăm finalizarea tuturor sarcinilor
int[] results = await Task.WhenAll(task1, task2, task3);
// obținem rezultatele:
foreach (int result in results)
   Console.WriteLine(result);

async Task<int> SquareAsync(int n)
{
   await Task.Delay(1000);
   return n * n;
}

În acest caz, metoda Square returnează un număr de tip int - pătratul numărului transmis metodei. Și variabila results va conține rezultatul apelului Task.WhenAll - de fapt rezultatele celor trei sarcini lansate. Deoarece toate sarcinile transmise la Task.WhenAll returnează int, rezultatul Task.WhenAll va fi un array de valori int.

De asemenea, după finalizarea unei sarcini, rezultatul acesteia poate fi obținut în mod standard prin proprietatea Result:

// definim și lansăm sarcinile
var task1 = SquareAsync(4);
var task2 = SquareAsync(5);
var task3 = SquareAsync(6);

await Task.WhenAll(task1, task2, task3);
// obținem rezultatul sarcinii task2
Console.WriteLine($"task2 result: {task2.Result}"); // task2 result: 25

async Task<int> SquareAsync(int n)
{
   await Task.Delay(1000);
   return n * n;
}
← Lecția anterioară Lecția următoare →