2

J'ai une liste lstSubscriptionRequests sur laquelle des éléments individuels je fais un traitement de manière asynchrone. Ensuite, après que tous les éléments sont traités, je dois retourner les éléments de liste mis à jour. Mon implémentation actuelle est commePartage de contexte d'objet Tâches

List<SubscriptionRequest> lstSubscriptionRequests = FromSomeResource(); 

var tsk = new List<Task>(); 
       foreach (var oTsk in lstSubscriptionRequests.Select(objSubscriptionRequest => new Task(
        () => ProcessSubscriptionForASingleRecord(objSubscriptionRequest)))) 
       { 
        oTsk.Start(); 
        lock (tsk) 
        { 
         tsk.Add(oTsk); 
        } 
       } 
       Task.WaitAll(tsk.ToArray()); 

Il est ressemble à certains des éléments après toutes les tâches accomplies ne sont pas mis à jour. S'il vous plaît laissez-moi savoir quelle correction j'avais besoin

+0

Ne créez pas une tâche par vous-même, utilisez 'wait' ou' Task.Run' Vos tâches ne commencent pas du tout – VMAtm

+0

Pouvez-vous m'aider à corriger le code ci-dessus? –

+0

Pourquoi créez-vous des tâches froides comme celle-ci et pourquoi utilisez-vous le verrouillage lorsque vous êtes * still * sur le thread principal? Votre code ne contient pas non plus 'Parallel.ForEach', ou n'importe quel code pour partager l'état.Il est impossible de répondre comme ceci.Par exemple' Parallel.ForEach (lstSubscriptionRequests, ProcessSubscriptionForASingleRecord) 'est équivalent à ceci Pourquoi n'utilisez-vous pas? Si 'ProcessSubscription..' produit des résultats, utilisez PLINQ, par exemple' from subscription dans lstSubscriptionRequests.AsParallel() sélectionnez ProcessSubscriptionForASingleRecord (abonnement) ' –

Répondre

2

Vous pourriez accomplir cela beaucoup plus facilement. Notez que l'utilisation du constructeur Task n'est généralement pas nécessaire ou recommandée, la mutation de l'état d'un objet particulier peut être difficile à suivre ou à déboguer. Retourner un nouvel objet qui représente votre état souhaité vous permettra d'appliquer un état valide minimum. Le code suivant traite tous vos éléments et renvoie les éléments terminés à votre code client.

using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 

namespace Processing { 
    public class MyProcessor { 

     public async Task<IEnumerable<Subscription>> ProcessSubscriptionRequestsAsync(IEnumerable<SubscriptionRequest> subscriptionRequests) { 
      var subscriptionProcessingTasks = subscriptionRequests.Select(request => ProcessSubscriptionForASingleRecord(request)).ToArray(); 
      return await Task.WhenAll(subscriptionProcessingTasks); 
     } 

     public async Task<Subscription> ProcessSubscriptionForASingleRecord(SubscriptionRequest request) { 
      //process the request 
      try { 
       var subscription = await Context.ProcessRequest(request); 
       return subscription; 
      } catch { 
       //something went wrong with the request 
      } 
     } 
    } 

    public class SubscriptionRequest { 
     //A subcription request 
    } 

    public class Subscription { 
     //A completed subscription request 
    } 
} 

Mise à jour

Il pourrait aider si vous pouvez exclure un nouvel abonnement de classe et ajouter la solution dans votre réponse. Je l'essaierai ensuite

Espérons que l'aperçu simplifié avant et après sera plus facile à intégrer. La principale différence est de remplacer le Parallel.ForEach avec un Select pour créer votre collection de tâches sans avoir besoin de prendre un verrou sur la liste des tâches pour chaque SubscriptionRequest, également de mettre en parallèle Task s en parallèle n'est généralement pas nécessaire car chacun d'entre eux sera exécuté de manière asynchrone seul gain atteindre un point où tous sont en attente plus tôt, pas finissant. Ensuite, chaque tâche est autorisée à démarrer et toutes sont attendues au await Task.WhenAll(tasks). Il sera important pour vous de déterminer quel type de traitement chaque SubscriptionRequest subit. Par souci d'exemple j "ai fait l'hypothèse que chaque demande est en quelque sorte lié à l'accès de base de données, à savoir le stockage de la demande, la mise à jour d'un profil utilisateur de toutes sortes etc ..

public class OriginalSynchronous { 

    public void ProcessSubscriptionRequest() { 
     List<SubscriptionRequest> lstSubscriptionRequests = FromSomeResource(); 

     List<Task> tsk = new List<Task>(); 
     Parallel.ForEach(lstSubscriptionRequests, objSubscriptionRequest => { 
      var oTsk = 
       new Task(
        () => ProcessSubscriptionForASingleRecord(objSubscriptionRequest));// update some properties after processing SubscriptionRequest 
      oTsk.Start(); 
      lock (tsk) { 
       tsk.Add(oTsk); 
      } 

     }); 
     Task.WaitAll(tsk.ToArray()); 
    } 

    private void ProcessSubscriptionForASingleRecord(SubscriptionRequest request) { 
     //modify SubscriptionRequest 
    } 
} 

public class ModifiedAsync { 

    public async Task ProcessSubscriptionRequest() { 
     var subscriptionRequests = await FromSomeResourceAsync(); 
     var tasks = subscriptionRequests.Select(request => { 
      return ProcessSubscriptionForASingleRecord(request); 
     }).ToArray(); 
     await Task.WhenAll(tasks); 
    } 

    public async Task ProcessSubscriptionForASingleRecord(SubscriptionRequest request) { 
     //modify SubscriptionRequest 
    } 
} 
+0

Je ne comprends pas pourquoi je dois faire un autre abonnement de classe? –

+0

@KamranShahid vous n'avez _have_ à, 'ProcessSubscriptionForASingleRecord' pourrait tout aussi bien modifier' SubscrptionRequest' comme votre code d'origine. Faire cela vous permettra de supprimer la création manuelle 'Task', inutile' lock', et l'inutile 'Parallel.ForEach' qui a causé des problèmes dans le code original. L'ajout de 'Subscription' était juste une suggestion pour un raffinement supplémentaire du code, ce n'est pas strictement nécessaire. – JSteward

+0

Cela pourrait vous aider si vous pouviez exclure un nouvel abonnement de classe et ajouter la solution dans votre réponse. Je vais ensuite l'essayer –