更新2011-05-20 12:49 AM: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 ;
}
感谢您的帮助!
+汤姆
答案 0 :(得分:7)
编辑:关于你的问题的更新。我没有发现任务是IO绑定的 - 并且可能所有文件都来自单个(传统?)磁盘。是的,这会变慢 - 因为你在一个不可并行化的资源中引入争用,迫使磁盘在所有地方寻找。
IO绑定任务仍然可以有效地并行化有时 - 但这取决于资源本身是否可并行化。例如,SSD(寻道时间小得多)可能完全改变您所看到的特性 - 或者如果您从几个单独的慢速服务器上获取网络,则可能是IO - 但不是在一个频道上。
您已创建了一个查询,但从未使用它。迫使所有内容与查询一起使用的最简单方法是使用Count()
或ToList()
或类似的东西。但是,更好的方法是使用Parallel.ForEach
:
var options = new ParallelOptions { MaxDegreeOfParallelism = sfcoll.Count() };
Parallel.ForEach(sfcoll, options, sf => ProcessShellObject(sf, curExeName));
我不确定如此设置最大并行度是正确的方法。 可能工作,但我不确定。处理此问题的另一种方法是将所有操作作为任务启动,指定TaskCreationOptions.LongRunning
。
答案 1 :(得分:1)
你应该在最后添加一行
var results = query.ToList();
答案 2 :(得分: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