Search code examples
c#parallel-processingparallel.foreach

How to populate and return List<T> from Parallel.ForEach using partitioning


I am trying to learn usage of Parallel.ForEach loop with partitioning. I encounter a code and try to modify it but getting error.

See one example code:

 namespace TaskPartitionExample
 {
     public partial class Form1 : Form
     {
         public Form1()
         {
             InitializeComponent();
         }
    
         private void button1_Click(object sender, EventArgs e)
         {
             List<Person> persons = GetPerson();
             int ageTotal = 0;
    
             Parallel.ForEach
             (
                 persons,
                 () => 0,
                 (person, loopState, subtotal) => subtotal + person.Age,
                 (subtotal) => Interlocked.Add(ref ageTotal, subtotal)
             );
    
             MessageBox.Show(ageTotal.ToString());
         }
    
         static List<Person> GetPerson()
         {
             List<Person> p = new List<Person>
             {
                 new Person() { Id = 0, Name = "Artur", Age = 5 },
                 new Person() { Id = 1, Name = "Edward", Age = 10 },
                 new Person() { Id = 2, Name = "Krzysiek", Age = 20 },
                 new Person() { Id = 3, Name = "Piotr", Age = 15 },
                 new Person() { Id = 4, Name = "Adam", Age = 10 }
             };
    
             return p;
         }
     }
    
     class Person
     {
         public int Id { get; set; }
         public string Name { get; set; }
         public int Age { get; set; }
     }
 }

I tried to change the above code to create new List<T> instance from localInit section of Parallel.ForEach and from body populate List<T> with data and from localFinally return the new List<T> but facing problem.

List<Person> persons1 = new List<Person>();
Parallel.ForEach(persons, new Person(), drow =>
    {
    },
    (persons1) => lock{}
    );

Please help me to do it. I want to populate a list from Parallel.ForEach with data from another list. I want to create a local List<T> which I like to populate with data from my global List<T>. How it will be possible? How can I declare a local list with in Parallel.ForEach localInit section? from body section I want to populate that local list with data from global List<T> and from localFinally block that want to return my local List<T> to outside. Please guide me to achieve this.


Solution

  • As an exercise you can do something like this:

    var persons1 = new List<Person>();
    var locker = new object();
    Parallel.ForEach(
        persons,
        () => new List<Person>(), // initialize aggregate per thread 
        (person, loopState, subtotal) =>
        {
            subtotal.Add(person); // add current thread element to aggregate 
            return subtotal; // return current thread aggregate
        },
        p => // action to combine all threads results
        {
            lock (locker) // lock, cause List<T> is not a thread safe collection
            {
                persons1.AddRange(p);
            }
        }
    );
    

    This should not be used in production code though. What you should do usually depends on actual task you want to perform.