49 votes

Parallel.ForEach bloque-t-il?

L' .fonction réseau Parallèle.ForEach bloquer le thread appelant? Je suppose que le comportement est l'un de ces:

  1. Oui, il bloque jusqu'à ce que le plus lent du point de l'exécution de retours.
  2. Non, il ne bloque pas et retourne le contrôle immédiatement. Les éléments à exécuter en parallèle sont effectuées sur les threads d'arrière-plan.

Ou peut-être quelque chose d'autre se passe, quelqu'un sait pour sûr?

Cette question s'est posée lors de la mise en œuvre de cette dans une classe de log:

public class MultipleLoggingService : LoggingServiceBase
{
    private readonly List<LoggingServiceBase> loggingServices;

    public MultipleLoggingService(List<LoggingServiceBase> loggingServices)
    {
        this.loggingServices = loggingServices;
        LogLevelChanged += OnLogLevelChanged;
    }

    private void OnLogLevelChanged(object sender, LogLevelChangedArgs args)
    {
        loggingServices.ForEach(l => l.LogLevel = LogLevel);
    }

    public override LogMessageResponse LogMessage(LogMessageRequest request)
    {
        if (request.LogMessage)
            Parallel.ForEach(loggingServices, l => l.LogMessage(request));

        return new LogMessageResponse{MessageLogged = request.LogMessage};
    }
}

Avis de l' LogMessage des appels de méthode de certains autres services de journalisation. J'ai besoin de cette partie de retourner immédiatement, afin de ne pas bloquer le thread appelant.


Mise à jour: Basé sur les commentaires des autres (nous avons confirmé le comportement est #1). Donc, j'ai pris des conseils pour utiliser la Tâche de la bibliothèque et réécrit la boucle comme ceci:

          if (request.LogMessage)
            foreach (var loggingService in loggingServices)
                Task.Factory.StartNew(() => loggingService.LogMessage(request));

61voto

Gabe Points 49718

Le numéro 1 est correct; Parallel.ForEach ne revient pas tant que la boucle n'est pas terminée. Si vous ne voulez pas ce comportement, vous pouvez simplement exécuter votre boucle en tant que Task et l'exécuter sur un autre thread.

10voto

Henk Holterman Points 153608

Re votre mise à jour, StartNew dans un foreach normal ():

Ce n'est peut-être pas le plus optimal pour les grandes collections, et vous n'obtenez pas de point pour gérer les erreurs.

Vos services de journalisation ne contiennent probablement pas des milliers d'éléments, mais la gestion des erreurs reste un point.

Considérer:

 Task.Factory.StartNew(() => 
{
   try
   {
        Parallel.ForEach(loggingServices, l => l.LogMessage(request));
   }
   catch(SomeException ex)
   {
       // at least try to log it ...
   }
});
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X