Parallel.for 会导致不同的结果

本文关键字:结果 for Parallel | 更新日期: 2023-09-27 18:27:02

我目前正在尝试改进我正在从事的 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(。

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

谢谢。

(因为我是新手,所以我不允许创建Parallel.For标签(

编辑:我又进行了一些测试。基本上,我在代码中查看的输出比以前更早。我的代码的并行版本似乎没有完全填充sg[][]数组。相反,某些值保留为其默认值(在我的例子中为 0(。

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

Parallel.for 会导致不同的结果

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

基本上,我不知道,parallel.for创建的线程没有继承 CultureInfo(这是线程的正常行为,我不知道(。当时发生的事情是,像3.256这样的字符串被解析为3256.0。这导致了我在输出中发现的问题。(注意:我计算机上的默认语言环境设置为使用逗号作为小数分隔符,但我已设置为程序中的句号.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;
});

感谢所有提出评论和意见的人。改善我的编程的好信息。

我更新了问题标签以反映问题的真正位置。

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

如果您将此代码段嵌套在另一个 Parallel.For(( 调用中,则可能会错过 lambda 表达式中N已关闭的事实,并且可能正在更新。 因此,当您停止更新"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;
});