c# - ¿Cómo puedo recopilar los valores devueltos de Parallel.ForEach?




c#-4.0 (5)

Estoy llamando a un servicio web lento en paralelo. Las cosas fueron geniales hasta que me di cuenta de que necesitaba obtener algo de información del servicio. Pero no veo dónde recuperar los valores. No puedo escribir en la base de datos, HttpContext.Current parece ser nulo dentro de un método llamado usando Parallel.ForEach

A continuación se muestra un programa de ejemplo (en su mente, imagine un servicio web lento en lugar de una concatenación de cadenas)

using System;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        WordMaker m = new WordMaker();
        m.MakeIt();
    }
    public class WordMaker
    {
        public void MakeIt()
        {
            string[] words = { "ack", "ook" };
            ParallelLoopResult result = Parallel.ForEach(words, word => AddB(word));
            Console.WriteLine("Where did my results go?");
            Console.ReadKey();
        }
        public string AddB(string word)
        {
            return "b" + word;
        }
    }

}

Answers

Esto parece seguro, rápido y simple:

    public string[] MakeIt() {
        string[] words = { "ack", "ook" };
        string[] results = new string[words.Length];
        ParallelLoopResult result =
            Parallel.For(0, words.Length, i => results[i] = AddB(words[i]));
        return results;
    }

Qué tal algo como esto:

public class WordContainer
{
    public WordContainer(string word)
    {
        Word = word;
    }

    public string Word { get; private set; }
    public string Result { get; set; }
}

public class WordMaker
{
    public void MakeIt()
    {
        string[] words = { "ack", "ook" };
        List<WordContainer> containers = words.Select(w => new WordContainer(w)).ToList();

        Parallel.ForEach(containers, AddB);

        //containers.ForEach(c => Console.WriteLine(c.Result));
        foreach (var container in containers)
        {
            Console.WriteLine(container.Result);
        }

        Console.ReadKey();
    }

    public void AddB(WordContainer container)
    {
        container.Result = "b" + container.Word;
    }
}

Creo que el bloqueo o los objetos concurrentes no son necesarios a menos que necesite los resultados para interactuar entre sí (como si estuviera calculando una suma o combinando todas las palabras). En este caso, ForEach divide cuidadosamente su lista original y le entrega a cada hilo su propio objeto para que pueda manipular todo lo que quiera sin preocuparse de interferir con los otros hilos.


Lo has descartado aquí.

ParallelLoopResult result = Parallel.ForEach(words, word => AddB(word));

Probablemente quieras algo como,

ParallelLoopResult result = Parallel.ForEach(words, word =>
{
    string result = AddB(word);
    // do something with result
});

Si desea algún tipo de colección al final de este, considere usar una de las colecciones en System.Collections.Concurrent , como ConcurrentBag

var resultCollection = new ConcurrentBag<string>();
ParallelLoopResult result = Parallel.ForEach(words, word =>
{
    resultCollectin.Add(AddB(word));
});

// Do something with result

No use ConcurrentBag para recopilar resultados, ya que es extremadamente lento. Use el bloqueo local en su lugar.

var resultCollection = new List<string>();
object localLockObject = new object();

Parallel.ForEach<string, List<string>>(
      words,
      () => { return new List<string>(); },
      (word, state, localList) =>
      {
         localList.Add(AddB(word));
         return localList;
      },
      (finalResult) => { lock (localLockObject) resultCollection.AddRange(finalResult); }
); 

// Do something with resultCollection here

En C # 6.0 esto es una brisa!

Puede hacerlo en la declaración de Class sí misma, en las declaraciones de declaración de propiedad.

public class Coordinate
{ 
    public int X { get; set; } = 34; // get or set auto-property with initializer

    public int Y { get; } = 89;      // read-only auto-property with initializer

    public int Z { get; }            // read-only auto-property with no initializer
                                     // so it has to be initialized from constructor    

    public Coordinate()              // .ctor()
    {
        Z = 42;
    }
}






c# c#-4.0 parallel.foreach