MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Stream-uri asincrone

Începând cu versiunea C# 8.0, în C# au fost adăugate stream-urile asincrone, care simplifică lucrul cu fluxurile de date în mod asincron. Deși asincronitatea în C# există de mult timp, metodele asincrone permiteau până acum obținerea unui singur obiect, atunci când operația asincronă era pregătită să furnizeze rezultatul.

Pentru returnarea mai multor valori în C# pot fi folosiți iteratori, dar aceștia au o natură sincronă, blochează firul apelant și nu pot fi utilizați în context asincron. Stream-urile asincrone evită această problemă, permițând obținerea mai multor valori și returnarea acestora pe măsură ce sunt disponibile în mod asincron.

În esență, un stream asincron reprezintă o metodă care are trei caracteristici:

  • Metoda are modificatorul async
  • Metoda returnează un obiect IAsyncEnumerable<T>. Interfața IAsyncEnumerable definește metoda GetAsyncEnumerator, care returnează IAsyncEnumerator:
public interface IAsyncEnumerable<out T>
{
   IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}

public interface IAsyncEnumerator<out T> : IAsyncDisposable
{
   T Current { get; }
   ValueTask<bool> MoveNextAsync();
}

public interface IAsyncDisposable
{
   ValueTask DisposeAsync();
}
  • Metoda conține expresii yield return pentru obținerea secvențială a elementelor din stream-ul asincron

De fapt, stream-ul asincron combină asincronitatea și iteratorii. Să analizăm un exemplu simplu:

await foreach (var number in GetNumbersAsync())
{
   Console.WriteLine(number);
}

async IAsyncEnumerable<int> GetNumbersAsync()
{
   for (int i = 0; i < 10; i++)
   {
       await Task.Delay(100);
       yield return i;
   }
}

Metoda GetNumbersAsync() reprezintă de fapt un stream asincron. Această metodă este asincronă. Tipul său returnat este IAsyncEnumerable<int>. Metoda returnează cu ajutorul yield return câte un număr la fiecare 100 de milisecunde. Astfel, metoda trebuie să returneze 10 numere de la 0 la 9 cu o pauză de 100 de milisecunde între ele.

Pentru a obține date din stream în metoda Main, se folosește ciclul foreach:

await foreach (var number in GetNumbersAsync())

Este important de menționat că ciclul este precedat de operatorul await. Astfel, de fiecare dată când stream-ul asincron va returna un nou număr, ciclul îl va prelua și îl va afișa pe consolă.

Unde pot fi utilizate stream-urile asincrone? Stream-urile asincrone pot fi utilizate pentru obținerea datelor dintr-un depozit extern. De exemplu, să presupunem că avem următoarea clasă a unui depozit:

class Repository
{
   string[] data = { "Tom", "Sam", "Kate", "Alice", "Bob" };
   public async IAsyncEnumerable<string> GetDataAsync()
   {
       for (int i = 0; i < data.Length; i++)
       {
           Console.WriteLine($"Obținem elementul {i + 1}");
           await Task.Delay(500);
           yield return data[i];
       }
   }
}

Pentru simplificarea exemplului, datele sunt reprezentate aici sub forma unui simplu array intern de șiruri de caractere. Pentru a imita întârzierea în obținerea datelor, se folosește metoda Task.Delay.

Să obținem aceste date în program:

Repository repo = new Repository();
IAsyncEnumerable<string> data = repo.GetDataAsync();
await foreach (var name in data)
{
   Console.WriteLine(name);
}

class Repository
{
   string[] data = { "Tom", "Sam", "Kate", "Alice", "Bob" };
   public async IAsyncEnumerable<string> GetDataAsync()
   {
       for (int i = 0; i < data.Length; i++)
       {
           Console.WriteLine($"Obținem elementul {i + 1}");
           await Task.Delay(500);
           yield return data[i];
       }
   }
}

În acest exemplu, se creează o instanță a clasei Repository și se obțin datele asincron folosind metoda GetDataAsync(). Folosind await foreach, se iterează prin elementele returnate de stream-ul asincron și se afișează pe consolă.

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