Parallel.for导致不同的结果

时间:2013-08-10 01:53:23

标签: c# task-parallel-library locale cultureinfo

我目前正在努力改进我正在开发的C#项目。具体来说,我的目标是并行化一些操作以减少处理时间。 我从小片段开始,只是为了得到它的悬念。 以下代码(非并行)可正常工作(按预期方式)

for (int i = 0; i < M; i++)
{
     double d;
     try
     {
          d = Double.Parse(lData[i]);
     }
     catch (Exception)
     {
         throw new Exception("Wrong formatting on data number " + (i + 1) + " on line " + (lCount + 1));
     }
     sg[lCount % N][i] = d;
}

通过使用以下(并行)代码,我希望获得完全相同的结果,但事实并非如此。

Parallel.For(0, M, i =>
{
    double d;
    try
    {
        d = Double.Parse(lData[i]);
    }
    catch (Exception)
    {
        throw new Exception("Wrong formatting on data number " + (i + 1) + " on line " + (lCount + 1));
    }
    sg[lCount % N][i] = d;
});

这些片段的程序部分来自文件,一次一行读取数据。每一行都是逗号分隔的双精度数字序列,我使用String.Split()放入向量lData []。每M行,数据序列以新的数据帧重新开始(因此,当我赋值时,元素索引中的% M。)

我的理解(显然是错误的)通过将(serial)for循环中的代码放在Parallel.For的第三个参数中,我将其执行并行化。这不应该改变结果。问题在于线程是否都访问了lCount和M?我应该制作线程本地副本吗?

感谢。

(因为我是新的,我不允许创建Parallel.For标签)

编辑: 我跑了一些测试。基本上我在代码中提前查看了输出,而不是之前的输出。似乎我的代码的并行版本没有完全填充sg[][]数组。相反,某些值保留为默认值(在我的情况下为0)。

编辑2(回答一些评论): lData[]是使用string[]获得的string.Split()。我正在拆分的原始字符串是从我的数据文件中读取的。我编写了生成它们的代码,因此它们通常格式良好(我仍习惯使用try-catch构造)。就在for循环(并行或串行)之前,我检查以确认lData[]具有正确的值数(M)。如果它没有,我抛出一个异常,阻止程序到达有问题的for循环。 sg[][]是类型double的N×M数组(片段中有拼写错误,现在已更正;在我的原始代码中,此错误不存在)。从文件中读取N行后,数组sg[][]包含整个数据集。在for循环(并行或串行)之后,有一部分看起来像这样:     lCount ++; //计算我已读过的行     if((lCount%N)== 0)     {         //用sg [] []做事         // reset sg [] []     } 所以,我故意覆盖sg[][]的所有行。 for-loop的整个目的是更新sg[][]中的值。

2 个答案:

答案 0 :(得分:3)

在周末进行了一些逐行调试后,我设法找到了问题所在。

基本上,我不知道,parallel.for创建的线程没有继承CultureInfo(这是线程的正常行为,我不知道)。接下来发生的事情是像3.256这样的字符串被解析为3256.0。这导致了我在输出中发现的问题。 (注意:我的计算机上的默认语言环境设置为使用逗号作为小数分隔符,但我已经在program.cs中设置了所有代码的完整停止。我错误地认为这将被新线程继承)

正确的并行代码段如下所示:

CultureInfo newCulture = (CultureInfo)CultureInfo.CurrentCulture.Clone();
newCulture.NumberFormat.NumberDecimalSeparator = ".";
Parallel.For(0, M, i =>
{
    Thread.CurrentThread.CurrentCulture = newCulture;
    double d;
    try
    {
        d = Double.Parse(lData[i]);
    }
    catch (Exception)
    {
        throw new Exception("Wrong formatting on data number " + (i + 1) + " on line " + (lCount + 1));
    }
    GlobalVar.sgData[lCount % N][i] = d;
});

感谢所有提出意见和建议的人。很好的信息,以改善我的编程。

我更新了问题标签,以反映问题的确切位置。

答案 1 :(得分:2)

据我所知,代码中的任何内容都不是本身错误。我的猜测是你在包含片段的函数中有一个竞争条件或闭包问题,可能在变量N上。

如果你在另一个Parallel.For()调用中嵌套了这个片段,你可能会错过N在lambda表达式中被关闭的事实,并且可能正在更新。因此,当您关闭更新'N'时,您希望它在lambda内保持不变。要解决此问题,请尝试以下方法:

// Create a local copy of N and M, so that if we update 
// it elsewhere it doesn't affect the closure
var n = N;
var m = M;
Parallel.For(0, m, i =>
{
    double d;
    try
    {
        d = Double.Parse(lData[i]);
    }
    catch (Exception)
    {
        throw new Exception("Wrong formatting on data number " + (i + 1) + " on line " + (lCount + 1));
    }
    sg[lCount % n][i] = d;
});