使用Parallel.ForEach进行调试或逐步执行

24
有没有一个简单的方法可以遍历 Parallel.ForEach 呢?什么是使用断点调试的最佳方式?

2
如果您不关心调试并行性,只想检查循环内部发生了什么,请将其复制到串行foreach中并逐步执行(在生产环境中取消并行注释)。 - Dimitri
@Dimitri 更安全、更简单的方法是将“Parallel.ForEach”调用顺序执行以进行调试。 - Reed Copsey
@Reed - 是的,这就是为什么我给你的答案点了赞。以前从未想过这个。 - Dimitri
8个回答

42

在调试期间,我经常将Parallel.ForEach设置为使用MaxDegreeOfParallelism设置为1。这样可以更轻松地进行调试。

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

然而,这并不能帮助调试与竞争条件或数据同步相关的问题,事实上,它经常会隐藏或消除代码中的真实问题。可以通过使用VS 2010中的新工具(例如Parallel Tasks window)或使用Debugging Multithreaded Applications中列出的各种技术(如switching threads、在步进时锁定线程等)来更轻松地调试这些问题。

你知道“-1”的任何文档吗? MSDN文章说,如果将其设置为0或小于-1的任何值,则会引发错误,但没有解释“-1”。 - granadaCoder
@granadaCoder 请参阅 https://msdn.microsoft.com/zh-cn/library/system.threading.tasks.paralleloptions.maxdegreeofparallelism(v=vs.110).aspx "如果值为-1,则没有限制同时运行的操作数量。" - Reed Copsey

11

与其他答案类似,我们在调试时将并行度设置为1,但是我们使用扩展方法实现此功能,例如:

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

    return pQuery;
}

然后,我们使用 .AsDebugFriendlyParallel() 替代使用 .AsParallel()


1
请注意,Parallel.ForEach()IEnumerable<T>.AsParallel() 并不完全相同 - https://dev59.com/92865IYBdhLWcg3wfemU。 - sventevit

7
你可以通过冻结除一个线程外的其他所有线程来使用Visual Studio获得类似的结果,在“线程”窗口中选择除了一个线程以外的所有线程,右键单击 -> 冻结,就像这样:

enter image description here

此外,如果你想要复现竞态条件而停在断点会破坏它,你可以始终添加跟踪点 - 使用Visual Studio或一些插件帮助你添加,如Oz Code。

enter image description here


5

如@PaulG所述,我认为最佳实践是将MaxDegreeOfParallelism的值设置为1。然后,通常Parallel也会像普通循环(例如ForForeach)一样工作。这是在Parallel上进行调试的更快方法。因此,您不需要在常规循环和Parallel之间切换代码。

Parallel.For(0, itemsList.Count, new ParallelOptions { MaxDegreeOfParallelism = 1 }, i =>
{
    //your process goes here
}

0

0

暂时将其重写为非并行foreach,或使用预处理器指令在调试模式下执行非并行代码。


0
我喜欢在断点上使用“When Hit”选项(右键单击断点,选择“When Hit...”)。您可以将变量的值、所在的线程等信息打印到控制台上。

0

这是我使用的策略,使得在调试模式下每个循环都按顺序执行。

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

“linksList”是什么?它是一个“List<Link>”吗? - Theodor Zoulias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接