Paul
Paul

Reputation: 3954

Syncing across nested ParallelFor loops

I have a class with a method that is running a nested ParallelFor loop. Basically I'm iterating over a list of objects and then a list contained in the properties of each object.

Based on a condition that is calculated for each object in the inner loop, I want to add to a queue. I'm using a "syncRoot" object in an attempt to maintain concurrency when adding to the queue.

public class ParallelTest
{
    private static object syncRoot = new object();

    public void Test() {
       List<MyLog> queue = new List<MyLog>();
       ...
       Parallel.For(0, set.Count(), delegate(int i)
       {
           var obj = set[i];
           List<Connection> conns = obj.GetConnections();
           ...      
           Parallel.For(0, conns.Count(), delegate(int j)
           {    
               Connection c = conns[j];
               MyLog log = new MyLog();             
               ...                  
               if (condition)
               {                    
                   lock (syncRoot)
                   {
                       queue.Add(log);
                   }
               }
           }
       }

       Debug.WriteLine(queue.Count);
   }
}

The problem I have is that it seems not all of my objects are getting added to the queue. I'm testing for a set of 200 objects and replacing the condition with true, so I would expect queue.Count to be 200. However, I get strange results ... sometimes 200, sometimes 198 or 199.

What am I doing wrong here? How do I ensure each thread is accounted for?

Upvotes: 0

Views: 130

Answers (2)

spender
spender

Reputation: 120508

This seems like an excellent candidate for Linq.

Assuming the code represented by ... could be encapsulated in a method with signature:

void Initialize(MyLog log, Connection conn, SomeUnknownType obj)

Your code could reduce to the following linq statement:

    var logs = set  
        .AsParallel()
        .SelectMany(
            obj =>
                obj.GetConnections()
                    .Select(conn => new{obj, conn}))
        .Select(x => { 
            var o = new{x.obj, x.conn, log = new MyLog()};
            Initialize(o.log, o.conn, o.obj); //or just do work inline
            return o;
        })
        .Where(x => x.obj... && x.conn...) //someCondition

    queue = logs.ToList();

Seeing as set.Count() is relatively high, parallelizing over set will ensure that the work is reasonably well divided over available cores. There's no need to parallelize again later on.

Upvotes: 1

Hamlet Hakobyan
Hamlet Hakobyan

Reputation: 33381

You can use ConcurrentQueue<T> Class instead List<T> . See http://msdn.microsoft.com/en-us/library/dd267265.aspx

for more information.

Upvotes: 0

Related Questions