Async e Await with For Loop

Ho un servizio di Windows che esegue vari lavori in base a una pianificazione. Dopo aver determinato quali lavori eseguire, un elenco di oggetti di pianificazione viene inviato a un metodo che scorre attraverso l’elenco ed esegue ogni lavoro. Il problema è che alcuni lavori possono richiedere fino a 10 minuti per essere eseguiti a causa di chiamate di database esterne.

Il mio objective è non avere un blocco di lavoro in coda, in pratica avere più di una corsa alla volta. Pensavo che usare async e attendere potesse essere una soluzione, ma non li ho mai usati prima.

Codice corrente:

public static bool Load(List scheduleList) { foreach (Schedule schedule in scheduleList) { Load(schedule.ScheduleId); } return true; } public static bool Load(int scheduleId) { // make database and other external resource calls // some jobs run for up to 10 minutes return true; } 

Ho provato ad aggiornare questo codice:

 public async static Task LoadAsync(List scheduleList) { foreach (Schedule schedule in scheduleList) { bool result = await LoadAsync((int)schedule.JobId, schedule.ScheduleId); } return true; } public async static Task LoadAsync(int scheduleId) { // make database and other external resource calls // some jobs run for up to 10 minutes return true; } 

Il problema è che il primo LoadAsync attende il completamento del processo prima di restituire il controllo al ciclo anziché consentire l’avvio di tutti i lavori.

Ho due domande:

  1. Alto livello – Are aysnc / attendono la scelta migliore, o dovrei usare un approccio diverso?
  2. Cosa deve essere aggiornato per consentire al loop di dare il via a tutti i lavori senza bloccare, ma non consentire alla funzione di tornare finché non vengono completati tutti i lavori?

Alto livello – Sono asincroni / attendono la scelta migliore, o dovrei usare un approccio diverso?

async-await è perfetto per ciò che si sta tentando di fare, il che sta simultaneamente offload di più attività associate all’IO.

Cosa deve essere aggiornato per consentire al loop di dare il via a tutti i lavori senza bloccare, ma non consentire alla funzione di tornare finché non vengono completati tutti i lavori?

Il tuo loop è attualmente in attesa perché LoadAsync ogni chiamata a LoadAsync . Quello che vuoi è eseguirli tutti contemporaneamente, piuttosto che aspettare che tutti finiscano di usare Task.WhenAll :

 public async static Task LoadAsync(List scheduleList) { var scheduleTaskList = scheduleList.Select(schedule => LoadAsync((int)schedule.JobId, schedule.ScheduleId)).ToList(); await Task.WhenAll(scheduleTaskList); return true; } 

Per le chiamate asincrone parallele fan-out, devi triggersre le attività per avviarle, ma poi gestirle come future asincrone o promettere valori. Puoi semplicemente sincronizzarli / aspettarli alla fine quando tutti sono finiti.

Il modo più semplice per farlo è trasformare il tuo ciclo in qualcosa di simile:

 List> jobs = new List>(); foreach (var schedule in scheduleList) { Task job = LoadAsync((int) schedule.JobId, schedule.ScheduleId); // Start each job jobs.Add(job); } bool[] finishedJobStatuses = await Task.WhenAll(jobs); // Wait for all jobs to finish running bool allOk = Array.TrueForAll(finishedJobStatuses, p => p); 

Consiglierei l’uso di Parallel.ForEach. Non è asincrono e esegue ogni iterazione in parallelo. Esattamente quello di cui hai bisogno.

 public static bool Load(IEnumerable scheduleList) { // set the number of threads you need // you can also set CancellationToken in the options var options = new ParallelOptions { MaxDegreeOfParallelism = 5 }; Parallel.ForEach(scheduleList, options, async schedule => { await Load(schedule.ScheduleId); }); return true; }