web-dev-qa-db-fra.com

Async et attente avec boucle

J'ai un service Windows qui exécute divers travaux en fonction d'un calendrier. Après avoir déterminé les travaux à exécuter, une liste d'objets de planification est envoyée à une méthode qui parcourt la liste et exécute chaque travail. Le problème est que certains travaux peuvent prendre jusqu'à 10 minutes pour s'exécuter en raison d'appels de base de données externes.

Mon objectif est de ne pas avoir un seul bloc de travail dans la file d’attente, mais d’en avoir plus d’un à la fois. Je pensais que l'utilisation de l'async et de l'attente pourrait résoudre ce problème, mais je ne les ai jamais utilisées auparavant.

Code actuel:

public static bool Load(List<Schedule> 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;
}

J'ai essayé de mettre à jour ce code:

public async static Task<bool> LoadAsync(List<Schedule> scheduleList)
{
    foreach (Schedule schedule in scheduleList)
    {
        bool result = await LoadAsync((int)schedule.JobId, schedule.ScheduleId);
    }

    return true;
}

public async static Task<bool> LoadAsync(int scheduleId)
{
    // make database and other external resource calls 
    // some jobs run for up to 10 minutes   

    return true;
}

Le problème est que le premier LoadAsync attend la fin du travail avant de redonner le contrôle à la boucle au lieu de permettre à tous les travaux de démarrer.

J'ai deux questions:

  1. Haut niveau - aysnc/attend-il le meilleur choix, ou dois-je utiliser une approche différente?
  2. Qu'est-ce qui doit être mis à jour pour permettre à la boucle de démarrer tous les travaux sans blocage, mais pas pour permettre à la fonction de revenir tant que tous les travaux ne sont pas terminés?
22
Josh

Niveau élevé - L'async/attend-il le meilleur choix, ou dois-je utiliser une approche différente?

async-await est parfait pour ce que vous essayez de faire, qui consiste à décharger simultanément plusieurs IO tâches liées.

Qu'est-ce qui doit être mis à jour pour permettre à la boucle de démarrer tous les travaux sans blocage, mais pas pour permettre à la fonction de revenir tant que tous les travaux ne sont pas terminés?

Votre boucle attend actuellement parce que vous await chaque appel à LoadAsync. Ce que vous voulez, c'est de les exécuter tous simultanément, puis d'attendre qu'ils finissent tous d'utiliser Task.WhenAll:

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

   return true;
}
28
Yuval Itzchakov

Pour les appels asynchrones parallèles sortants, vous souhaitez déclencher les tâches pour les lancer, mais les traiter ensuite comme des valeurs asynchrones futures ou promises. Vous pouvez simplement les synchroniser/les attendre à la fin lorsque tout est terminé.

La façon la plus simple de le faire est de transformer votre boucle for en quelque chose comme ceci:

List<Task<bool>> jobs = new List<Task<bool>>();
foreach (var schedule in scheduleList)
{
    Task<bool> 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);
4
Jorgen Thelin