24

Is there an easy way to step through a parallel.foreach? What is the best way to debug this with a break point?

moribvndvs
  • 42,191
  • 11
  • 135
  • 149
Arcadian
  • 4,312
  • 12
  • 64
  • 107
  • 2
    if you don't care to debug parallelism, and just want to check what is happening inside the loop, copy it to a serial foreach and step through it (commenting out the parallel for production) – Dimitri Jun 19 '12 at 20:39
  • @Dimitri Safer/simpler to just make `Parallel.ForEach` run sequentially for debugging. – Reed Copsey Jun 19 '12 at 20:45
  • @Reed - yep that's why i upvoted your answer. Never thought of it before – Dimitri Jun 19 '12 at 21:30

8 Answers8

41

During debug, I'll often setup my Parallel.ForEach to run with MaxDegreeOfParallelism set to 1. This makes it far simpler to debug.

const bool forceNonParallel = true;
var options = new ParallelOptions { MaxDegreeOfParallelism = forceNonParallel ? 1 : -1 };
Parallel.ForEach(collection, options, item => 
{ //...

However, this will not help with debugging issues relating to race conditions or data synchronization, and will in fact often hide or eliminate real problems in your code.

Those issues can often be debugged much more easily by using the new tools in VS 2010, such as the Parallel Tasks window, or by using the various techniques listed in Debugging Multithreaded Applications, such as switching threads, locking threads while stepping, etc.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • Do you know of any documentation for "-1" ? The msdn article says that an error will be thrown if it is set to 0 or any value below -1, but doesn't explain the -1. – granadaCoder Apr 28 '15 at 18:44
  • @granadaCoder See https://msdn.microsoft.com/en-us/library/system.threading.tasks.paralleloptions.maxdegreeofparallelism%28v=vs.110%29.aspx "If it is -1, there is no limit on the number of concurrently running operations." – Reed Copsey Apr 28 '15 at 19:22
11

Similar to other answers here, we set degree of parallelism to 1 when debugging, but we do this with an extension method, like:

public static ParallelQuery<TSource> AsDebugFriendlyParallel<TSource>(this IEnumerable<TSource> source)
{
    var pQuery = source.AsParallel();
    #if DEBUG
    pQuery = pQuery.WithDegreeOfParallelism(1);
    #endif

    return pQuery;
}

Then, instead of using .AsParallel() we use .AsDebugFriendlyParallel()

PaulG
  • 13,871
  • 9
  • 56
  • 78
  • 1
    Please note that `Parallel.ForEach()` and `IEnumerable.AsParallel()` are not quite the same - [https://stackoverflow.com/questions/3789998/parallel-foreach-vs-foreachienumerablet-asparallel](https://stackoverflow.com/questions/3789998/parallel-foreach-vs-foreachienumerablet-asparallel) – sventevit Feb 03 '20 at 08:45
7

You can actually get similar results with Visual Studio just by freezing all the threads except one, select all threads but one in the Threads windows and right click -> Freeze like this:

enter image description here

Also, if you want to reproduce a race condition and stopping on breakpoints breaks it, you can always add tracepoints - either with visual studio or with plugins that help with it, such as Oz Code

enter image description here

Stas Sh
  • 656
  • 4
  • 8
5

As @PaulG answered i think best practice is just set MaxDegreeOfParallelism value to 1. Then normally Parallel also will work similar to regular loop like For, Foreach. This is the faster way to debug on Parallel. So you don't need to switch code between regular loop and Parallel.

Parallel.For(0, itemsList.Count, new ParallelOptions { MaxDegreeOfParallelism = 1 }, i =>
{
    //your process goes here
}
Liakat Hossain
  • 1,288
  • 1
  • 13
  • 24
0

Temporarily rewrite it as a non-parallel foreach, or use preprocessor directives to execute non-parallel code when running in debug mode.

Bob Black
  • 2,375
  • 2
  • 18
  • 26
0

I like to use the "When Hit" option on a breakpoint (right-click the breakpoint, select "When Hit...". You can print a message to the console that includes values of variables, the thread you are on, etc.

Kevin Aenmey
  • 13,259
  • 5
  • 46
  • 45
0

OzCode will help you a lot, it has a feature like tracepoints on steroids that is super useful when debuging concurrent\parallel code https://www.youtube.com/watch?v=_vuMi-3jGwY

0

This is the strategy I use which makes every Loop go sequential when in debug mode

 var parOpts = new ParallelOptions { MaxDegreeOfParallelism = -1 }; //No limit to parallel degree

#if DEBUG
            parOpts.MaxDegreeOfParallelism = 1; //Set parallel to 1
#endif

        Parallel.ForEach(links, node =>
        {
            string url = node.Attributes["href"].Value;
            Link link = ParseLink(url);
            link.LinkText = node.InnerText;

            if (link.Domain == RootLink.Domain)
            {
                if (link.Page == RootLink.Page)
                    link.Type = LinkType.Section;
                else
                    link.Type = LinkType.Internal;
            }
            else
                link.Type = LinkType.External;

            linksList.Add(link);
        });
Cyril Gupta
  • 13,505
  • 11
  • 64
  • 87