increment a count value outside parallel.foreach scope

27,791

Solution 1

I like to beat dead horses! :)

The "lightest" way to increment the count from multiple threads is:

Interlocked.Increment(ref count);

But as others have pointed out: if you're doing it inside Parallel.ForEach then you're probably doing something wrong.

I'm suspecting that for some reason you're using ForEach but you need an index to the item you're processing (it will never work with Parallel.ForEach). Am I close? Why do you need the count? What sort of VooDoo magic are you trying to do?

UPDATE:

You seem to be safe with the ConcurrentDictionary if your key is the URI and the value is TAnswer. I don't see a problem as long as you don't try to use the count to reference elements in your collection.

Finally...

If you need a counter, then use the Parallel.For loop... it safely increments the counter for you.

Solution 2

Use Interlocked.Increment method in this way.

int count = 0;
Parallel.ForEach(users, (u) =>
{
    var currentCount = Interlocked.Increment(ref count);
    Log(String.Format("Step {0} of {1}", currentCount, users.Count));
});

Solution 3

Use Interlocked.Increment.

I wouldn't increment things inside a parallel foreach (unless, of course, you're using Interlocked.Increment or some other locking mechanism). That's not what it's for. The Parallel foreach should be run only with actions that cause no side effects in shared state. To increment a value in a parallel foreach will cause the very problem you're more than likely trying to avoid.

Solution 4

Parallel.Foreach comes along with an extra overload that perfectly fits for that kind of scenario when you just want to count things inside the loop.

    int totalCount = 0;
    Parallel.ForEach(files, () => 0, (file, loopState, localCount) =>
    {
        if(ProcessFile(file))
           localCount++;
    
        return localCount;
    
    }, localCount => Interlocked.Add(ref totalCount, localCount));

    Console.WriteLine($"Processed {totalCount}/{files.Length} files.");

Into the loop body you can inject a thread local count varialble and increment/decrement it as usual. This way you do not have an InterLocked call in every loop cycle. Instead there is just one Interlocked.Add call at the end of each parallel Task summing up totalCount.

Note that with this method you should not use totalCount from within the loop body. If your problem requires to access totalCount from within the loop body you have to use the InterLocked.Incerment method as describe in other answers.

Share:
27,791
Cetin Sert
Author by

Cetin Sert

The Swiss clock maker among software developers - infinite passion for communicating powerful ideas succinctly and appreciation of beauty in well-organized code. Loves frontend work with vanilla JS / TypeScript functional programming in F#, Haskell symbolic programming in Mathematica cross-platform programming in Go WebAssembly programming via Rust <pdf-file><pdf-page> Professional PDF web componentsin the form of HTML custom elements. ▶️ playground <script src="//webpdf.co/<>" type=module></script><pdf-file id=f src=//pdf.ist/form.pdf></pdf-file><pdf-page of=f svg></pdf-page><button onclick=f.save()>Save</button> WebPDF.pro made with ❤️ by PDF.ist 📰 //blog.PDF.ist      (latest news, insights) ▶️ //try.WebPDF.pro      (playground, examples) 🌐 //WebPDF.pro      (homepage, features) 📖 //WebPDF.pro/#📖      (documentation) 🐣 twitter.com/WebPDFpro 📘 facebook.com/WebPDF.pro 📧 [email protected]

Updated on July 19, 2020

Comments

  • Cetin Sert
    Cetin Sert almost 4 years

    How can I increment an integer value outside the scope of a parallel.foreach loop? What's is the lightest way to synchronize access to objects outside parallel loops?

    var count = 0;
    Parallel.ForEach(collection, item =>
    {
      action(item);
      // increment count??
    }