How do I collect return values from Parallel.ForEach?

You've discarded it in here.

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

You probably want something like,

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

If you want some sort of collection at the end of this, consider using one of the collections under System.Collections.Concurrent, like ConcurrentBag

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

// Do something with the result

Your may consider using AsParallel extension method of IEnumerable, it will take care of the concurrency for you and collect the results.

words.AsParallel().Select(AddB).ToArray()

Synchronisation (e.g. locks or concurrent collections that use locks) are usually bottleneck of concurrent algorithms. The best is to avoid synchronisation as much as possible. I am guessing that AsParallel uses something smarter like putting all the items produced on single thread into a local non-concurrent collection and then combining these at the end.


Do not use ConcurrentBag to collect results as it is slower. Use local lock instead.

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

This seems safe, fast, and 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;
    }

How about something like this:

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;
    }
}

I believe the locking or concurrent objects isn't necessary unless you need the results to interact with one another (like you were computing a sum or combining all the words). In this case ForEach neatly breaks your original list up and hands each thread its own object that it can manipulate all it wants without worrying about interfering with the other threads.


Comments

  1. Mohammad

    • 2018/1/21

    You've discarded it in here. ParallelLoopResult result = Parallel.ForEach(words, word => AddB(word));. You probably want something like,

  2. Matias

    • 2015/4/30

    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

  3. Warren

    • 2019/7/15

    How do I collect return values from Parallel.ForEach? I'm calling a slow webservice in parallel. Things were great until I realized I need to get some 

  4. Farina

    • 2015/9/10

    Use local lock instead. 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.

  5. Issac

    • 2017/12/7

    How do I collect return values from Parallel.ForEach? Solution: You've discarded it in here. ParallelLoopResult result = Parallel.ForEach(words, word => 

  6. Jonas

    • 2019/6/7

    How do I collect return values from Parallel.ForEach? 187. Parallel.ForEach vs Task.Run and Task.WhenAll. Hot Network Questions

  7. Alexzander

    • 2019/4/29

    ForEach loop over any IEnumerable or IEnumerable data source. ToList(); /// <summary> /// GetPrimeListWithParallel returns Prime numbers 

  8. Lucca

    • 2019/12/11

    Parallel.ForEach<uspGetEmployees_Result> ( emps, async (e) => { e.Avatar = await Task.Run ( () => BuildUserFilePath (e.Avatar, e.UserId, httpCurrent, true)); } ); You're already using await on the result of the entire method, and you've used a Parallel.ForEach to get parallel execution of items in your loop, so what does the additional use of await Task.Run () get you?

  9. Rocky

    • 2018/3/2

    Your delegate returns the partition-local variable, which is then passed to the next iteration of the loop that executes in that particular 

  10. Palmieri

    • 2015/12/25

    I am experimenting with Parallel.ForEach in C# and SQL-Server Database Connectivity and table creation. By and large, trying to create a large number of tables using Parallel.ForEach. Although the code is running, I notice data leakage as not all tables are created. I am reading data from Microsoft.Data.Analysis DataFrame which is IEnumerable.

  11. Forrest

    • 2020/1/28

    Parallel ForEach in C# provides a parallel version of the standard, sequential foreach Garbage Collector in . C# Task Return Value.

  12. Alaric

    • 2016/9/3

    foreach (int i in integerList) {. long total = DoSomeIndependentTimeconsumingTask(); Console.WriteLine(" {0} - {1}", i, total); }; DateTime EndDateTime = DateTime.Now; Console.WriteLine(@"foreach Loop end at : {0}", EndDateTime); TimeSpan span = EndDateTime - StartDateTime; int ms = (int)span.TotalMilliseconds;

  13. Callahan

    • 2020/2/11

    Add(new BarcodedImage(barcode,image));; }; return barcodedImages;; } Using Parallel.ForEach. I then started to think this may be a good 

  14. Foster

    • 2017/3/24

    ForEach method enables parallel processing over a sequence of values. int ParallelSum ( IEnumerable < int > values ) { return values . AsParallel ().

  15. Ledger

    • 2019/5/6

    However, Action(Of T) specifies a Sub (i.e., a method that has no return value). While you could call the Parallel.For() and Parallel.ForEach() methods and 

Comments are closed.

Recent Posts