【问题标题】:Parallel.for causes different resultsParallel.for 导致不同的结果
【发布时间】:2013-08-12 01:12:07
【问题描述】:

我目前正在尝试改进我正在从事的 C# 项目。具体来说,我的目标是并行化一些操作以减少处理时间。 我从小的 sn-ps 开始只是为了掌握它。 以下代码(非并行)正常工作(如预期)

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

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

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

谢谢。

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

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

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

【问题讨论】:

  • 旁白:您可以通过使用Double.TryParse 而不是异常处理来提高性能。
  • 谢谢@James,我不知道。
  • 对于缺失值,您收到异常了吗?此外,如果您的处理取决于顺序,Parallel.For 不会按特定顺序执行。
  • @James:那不是真的。假设提供了有效输入,Try/catch 和 Double.TryParse 应该以相同的速度运行。当没有抛出异常时,try/catch 对性能没有影响。 msdn.microsoft.com/en-us/library/… 从他的例子来看,假设值有效且异常很少见。 stackoverflow.com/a/586539/455493 但除此之外,我同意。 Double.TryParse 是一种更清洁的方式。不一定更快。
  • 你是对的。这取决于异常无效数据的程度。

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


【解决方案1】:

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

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

正确的并行 sn-p 如下所示:

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

感谢所有参与 cmets 和意见的人。改进我的编程的好信息。

我更新了问题标签以反映问题所在。

【讨论】:

【解决方案2】:

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

如果您将此 sn-p 嵌套在另一个 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;
});

【讨论】:

  • 你好。我试过这个解决方案。它没有改变并行 for 循环的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-30
相关资源
最近更新 更多