1

2011 年 5 月 20 日上午 12:49 更新:对于我的应用程序,foreach 仍然比并行解决方案快 25%。并且不要将收集计数用于最大并行度,使用更接近机器上核心数量的东西。

=

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

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

foreach (ShellObject sf in sfcoll)
    ProcessShellObject(sf, curExeName);

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

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

谢谢你的帮助!

+汤姆

4

3 回答 3

7

编辑:关于您的问题的更新。我没有发现该任务是受 IO 限制的 - 大概所有文件都来自单个(传统?)磁盘。是的,这会变慢 - 因为您在不可并行的资源中引入了争用,迫使磁盘到处寻找。

IO-bound 任务有时仍然可以有效地并行化——但这取决于资源本身是否可并行化。例如,SSD(寻道时间更短)可能会完全改变您所看到的特性 - 或者如果您通过网络从多个单独的慢速服务器获取,您可能会受到 IO 限制,但不是在单个服务器上渠道。


您已经创建了一个查询,但从未使用过它。强制所有内容与查询一起使用的最简单方法是使用Count()orToList()或类似的东西。但是,更好的方法是使用Parallel.ForEach

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

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

于 2011-05-20T07:06:17.020 回答
1

你是否应该在最后添加一行

var results = query.ToList();
于 2011-05-20T07:06:55.420 回答
1

您通过 LINQ 创建的查询对象是一个 IEnumerable。仅当您枚举它时才会对其进行评估(例如,通过 foreach 循环):

        var query = from sf in sfcoll.AsParallel().WithDegreeOfParallelism(sfcoll.Count())
                    let p = ProcessShellObject(sf, curExeName)
                    select p;
        foreach(var q in query) 
        {
            // ....
        }
        // or:
        var results = query.ToArray(); // also enumerates query
于 2011-05-20T07:07:22.550 回答