C # Eseguendo molte attività asincrone contemporaneamente

Sono un po ‘nuovo per i compiti asincroni.

Ho una funzione che prende lo studente ID e raschia i dati da un sito web universitario specifico con l’ID richiesto.

private static HttpClient client = new HttpClient(); public static async Task ParseAsync(string departmentLink, int id, CancellationToken ct) { string website = string.Format(departmentLink, id); try { string data; var stream = await client.GetAsync(website, ct); using (var reader = new StreamReader(await stream.Content.ReadAsStreamAsync(), Encoding.GetEncoding("windows-1256"))) data = reader.ReadToEnd(); //Parse data here and return Student. } catch (Exception ex) { Console.WriteLine(ex.Message); } } 

E funziona correttamente. A volte però ho bisogno di eseguire questa funzione per molti studenti, quindi uso quanto segue

  for(int i = ids.first; i  { Dispatcher.Invoke(() => { listview_students.Items.Add(t.Result); //Students.Add(t.Result); //lbl_count.Content = $"{listview_students.Items.Count}/{testerino.Length}"; }); }); } 

Sto memorizzando le attività in un array per attendere in seguito.

Questo funziona anche se il numero di studenti è compreso tra (0, ~ 600?). È piuttosto casuale. E poi per ogni altro studente che non è ancora stato analizzato. Un’attività è stata annullata .

Tieni presente che non uso mai il token di cancellazione.

Ho bisogno di eseguire questa funzione su così tanti studenti che può raggiungere complessivamente ~ 9000 task asincroni. Quindi cosa sta succedendo?

Stai praticamente creando un attacco denial of service sul sito web quando stai facendo la coda su 9000 richieste in così poco tempo. Questo non solo causa errori, ma potrebbe anche distruggere il sito web. Sarebbe meglio limitare il numero di richieste concorrenti a un valore più ragionevole (ad esempio 30). Mentre ci sono probabilmente diversi modi per farlo, uno che viene in mente è il seguente:

 private async Task Test() { var tasks = new List(); for (int i = ids.first; i <= ids.last; i++) { tasks.Add(/* Do stuff */); await WaitList(tasks, 30); } } private async Task WaitList(IList tasks, int maxSize) { while (tasks.Count > maxSize) { var completed = await Task.WhenAny(tasks).ConfigureAwait(false); tasks.Remove(completed); } } 

Altri approcci potrebbero sfruttare il modello produttore / consumatore utilizzando classi .Net come BlockingCollection

Questo è quello che ho finito in base al codice @erdomke:

  public static async Task ForEachParallel( this IEnumerable list, Func action, int dop) { var tasks = new List(dop); foreach (var item in list) { tasks.Add(action(item)); while (tasks.Count >= dop) { var completed = await Task.WhenAny(tasks).ConfigureAwait(false); tasks.Remove(completed); } } // Wait for all remaining tasks. await Task.WhenAll(tasks).ConfigureAwait(false); } // usage await Enumerable .Range(1, 500) .ForEachParallel(i => ProcessItem(i), Environment.ProcessorCount);