Parallel.For循环冻结

时间:2012-08-09 12:53:43

标签: c# datatable parallel-processing

我正在尝试在并行中向DataTable添加一些信息但是如果循环是长时间它会冻结或者只是需要花费很多时间,比通常的循环时间更多,这是我的并行代码。对于循环:

Parallel.For(1, linii.Length, index =>
                 {
                     DataRow drRow = dtResult.NewRow();
                     alResult = CSVParser(linii[index], txtDelimiter, txtQualifier);

                     for (int i = 0; i < alResult.Count; i++)
                     {
                         drRow[i] = alResult[i];
                     }
                     dtResult.Rows.Add(drRow);
                 }
             );

怎么了?这个Parallel.For循环比正常循环花费的时间多得多,出了什么问题?

谢谢!

2 个答案:

答案 0 :(得分:5)

你不能改变来自2个不同线程的DataTable;它错误。 DataTable使无尝试成为线程安全的。所以:不要那样做。从一个线程做到这一点。很可能你受到IO的限制,所以你应该在一个线程上作为流来做。看起来你正在处理文本数据。您似乎有string[]行,可能是File.ReadAllLines()?嗯,这里非常糟糕:

  1. 它强制所有内容加载到内存中
  2. 你必须等待才能将所有内容加载到内存中
  3. CSV是一种多行格式;不保证1行== 1行
  4. 应该做的是使用代码项目中的CsvReader之类的东西,但即使你想一次只使用一行,也要使用StreamReader:

    using(var file = File.OpenText(path)) {
        string line;
        while((line = file.ReadLine()) != null) {
            // process this line
            alResult = CSVParser(line, txtDelimiter, txtQualifier);
    
            for (int i = 0; i < alResult.Count; i++)
            {
                drRow[i] = alResult[i];
            }
            dtResult.Rows.Add(drRow);
        }
    }
    

    使用Parallel会更快,所以我没有尝试这样做。 IO是你的瓶颈。锁定是一种选择,但它不会对你有很大的帮助。

    作为一个无关的方面,我注意到alResult未在循环内声明。这意味着在原始代码中alResult是一个捕获的变量,它在 所有 循环迭代之间共享 - 这意味着你是已经可怕地覆盖每一行。


    编辑:说明为什么Parallel与从文件中读取1,000,000行无关的原因:

    方法1:使用ReadAllLines加载行,然后使用Parallel来处理它们;这花费了物理文件IO的[固定时间],而然后我们并行。 CPU工作很少,我们基本上花了[固定时间]。但是,我们添加了大量的线程开销和内存开销,我们甚至无法启动,直到所有文件都被加载。

    方法2:使用流API;逐行读取每一行 - 处理每一行并添加它。这里的成本基本上是:[固定时间]用于加载文件的实际IO带宽。但;我们现在没有线程开销,没有同步冲突,没有大量内存要分配,我们立即开始填充表

    方法3:如果确实需要,第三种方法是读取器/写入器队列,其中一个专用线程处理文件IO并将这些行排入队列,第二个方法执行{{1 }}。坦率地说,它是更多移动部件,第二个线程将花费95%的时间等待来自文件的数据;坚持方法2!

答案 1 :(得分:1)

Parallel.For(1, linii.Length, index =>
{
  alResult = CSVParser(linii[index], txtDelimiter, txtQualifier);

  lock (dtResult)
  {
    DataRow drRow = dtResult.NewRow();
    for (int i = 0; i < alResult.Count; i++)
    {
       drRow[i] = alResult[i];
    }
    dtResult.Rows.Add(drRow);
  }
});