将此foreach循环转换为并行执行的正确PLINQ语法是什么?

Tom*_*m A 1 c# parallel-processing plinq

更新2011-05-20 12:49 AM:foreach仍然比我的应用程序的并行解决方案快25%.并且不要使用最大并行度的集合计数,使用更接近机器核心数量的东西.

=

我有一个IO绑定任务,我想并行运行.我想对文件夹中的每个文件应用相同的操作.在内部,该操作导致Dispatcher.Invoke将计算的文件信息添加到UI线程上的集合.因此,从某种意义上说,工作结果是方法调用的副作用,而不是直接从方法调用返回的值.

这是我想要并行运行的核心循环

foreach (ShellObject sf in sfcoll)
    ProcessShellObject(sf, curExeName);
Run Code Online (Sandbox Code Playgroud)

这个循环的上下文在这里:

        var curExeName = Path.GetFileName(Assembly.GetEntryAssembly().Location);
        using (ShellFileSystemFolder sfcoll = ShellFileSystemFolder.FromFolderPath(_rootPath))
        {
            //This works, but is not parallel.
            foreach (ShellObject sf in sfcoll)
                ProcessShellObject(sf, curExeName);

            //This doesn't work.
            //My attempt at PLINQ.  This code never calls method ProcessShellObject.

            var query = from sf in sfcoll.AsParallel().WithDegreeOfParallelism(sfcoll.Count())
                        let p = ProcessShellObject(sf, curExeName)
                        select p;
        }

    private String ProcessShellObject(ShellObject sf, string curExeName)
    {
        String unusedReturnValueName = sf.ParsingName
        try
        {
            DesktopItem di = new DesktopItem(sf);
            //Up date DesktopItem stuff
            di.PropertyChanged += new PropertyChangedEventHandler(DesktopItem_PropertyChanged);
            ControlWindowHelper.MainWindow.Dispatcher.Invoke(
                (Action)(() => _desktopItemCollection.Add(di)));
        }
        catch (Exception ex)
        {
        }
        return unusedReturnValueName ;
    }
Run Code Online (Sandbox Code Playgroud)

谢谢你的帮助!

+汤姆

Jon*_*eet 7

编辑:关于您的问题的更新.我没有发现任务是IO绑定的 - 并且可能所有文件都来自单个(传统?)磁盘.是的,这会变慢 - 因为你在不可并行化的资源中引入争用,迫使磁盘在所有地方寻找.

IO绑定任务有时仍可以有效地并行化- 但这取决于资源本身是否可并行化.例如,SSD(搜索时间要小得多)可能会完全改变您所看到的特性 - 或者如果您从几个单独的慢速服务器上获取网络,则可能是IO绑定但不是单个渠道.


您已经创建了一个查询,但从未使用它.强制一切与查询一起使用的最简单方法是使用Count()or ToList()或类似的东西.但是,更好的方法是使用Parallel.ForEach:

var options = new ParallelOptions { MaxDegreeOfParallelism = sfcoll.Count() };
Parallel.ForEach(sfcoll, options, sf => ProcessShellObject(sf, curExeName));
Run Code Online (Sandbox Code Playgroud)

我不确定设置这样的最大并行度是不是正确的方法.它可能有用,但我不确定.处理此问题的另一种方法是将所有操作作为任务启动,指定TaskCreationOptions.LongRunning.

  • @Tom:我已经编辑了我的答案来纠正ForEach调用(它*只是参数的顺序)并评论你的更新.请注意,使用ForEach确实比PLINQ更清洁 - 从根本上说你不是在尝试查询,而是尝试为集合中的每个*元素执行*.使用最适合工作的工具非常重要,而不是围绕特定工具的自然焦点工作,例如通过强制使用`ToList`进行评估. (2认同)