1

我正在尝试利用 .NET Framework 4.0 中的并行 for 循环。但是我注意到,我在结果集中缺少一些元素。

我有如下代码片段。lhs.ListData是一个可以为空的双精度列表,而rhs.ListData是一个可以为空的双精度列表。

int recordCount = lhs.ListData.Count > rhs.ListData.Count ? rhs.ListData.Count : lhs.ListData.Count;

List<double?> listResult = new List<double?>(recordCount);
var rangePartitioner = Partitioner.Create(0, recordCount);  

Parallel.ForEach(rangePartitioner, range =>
                    {
                        for (int index = range.Item1; index < range.Item2; index++)
                        {
                            double? result = lhs.ListData[index] * rhs.ListData[index];
                            listResult.Add(result);
                        }
                    });

lhs.ListData的长度为 7964,rhs.ListData的长度为 7962。当我执行“ * ”操作时,listResult 的输出只有 7867。两个输入列表中都有空元素。

我不确定执行期间发生了什么。为什么我在结果集中看到更少的元素有什么原因吗?请指教...

4

3 回答 3

2

正确的方法是使用 LINQ 的IEnumerable.AsParallel()扩展。它为您完成所有分区,并且 PLINQ 中的所有内容本质上都是线程安全的。还有另一个称为 LINQ 扩展Zip,它根据您提供的函数将两个集合压缩为一个。但是,这并不完全是您所需要的,因为它只是两个列表中较短的长度,而不是较长的长度。这样做可能很容易,但首先通过null在列表末尾填充两个列表中较短的一个,将其扩展为较长一个的长度。

IEnumerable<double?> lhs, rhs;    // Assume these are filled with your numbers.
double?[] result = System.Linq.Enumerable.Zip(lhs, rhs, (a, b) => a * b).AsParallel().ToArray();

这是 MSDN 页面Zip

http://msdn.microsoft.com/en-us/library/dd267698%28VS.100%29.aspx

于 2012-10-09T02:42:42.077 回答
0

这可能是因为List<T>(例如Add)上的操作不是线程安全的 - 您的结果可能会有所不同。作为一种解决方法,您可以使用锁,但这会大大降低性能。

看起来您只是希望结果列表中的每个项目都是两个输入列表中相应索引处的项目的乘积,而不是使用 PLINQ 怎么样:

var listResult = lhs.AsParallel()
                    .Zip(rhs.AsParallel(), (a,b) => a*b)
                    .ToList();

不知道你为什么在这里选择并行,如果这甚至是必要的,我会进行基准测试——这真的是你应用程序的瓶颈吗?

于 2012-10-09T02:20:45.713 回答
0

List<double?>用于存储结果,但Add方法不是线程安全的。您可以使用显式索引来存储结果(而不是调用Add):

listResult[index] = result;
于 2012-10-09T02:33:51.160 回答