代码之家  ›  专栏  ›  技术社区  ›  SilverLight

Parallel.ForEach问题-在文件上读写-文件正在使用错误

  •  2
  • SilverLight  · 技术社区  · 12 年前

    我真的很困惑平行。对于每个…它是如何工作的?
    下面的代码有一个错误-> File Is In Use

    Parallel.ForEach(list_lines_acc, (line_acc, list_lines_acc_state) =>
    {
         FileStream file = 
             new FileStream(GPLfilePath, FileMode.Open, FileAccess.ReadWrite);
         StreamReader reader = new StreamReader(file);
         var processed = string.Empty;
         Ok_ip_port = string.Empty;
         while (reader.EndOfStream)
         {
             if (string.IsNullOrEmpty(Ok_ip_port))
             {
                 Ok_ip_port = reader.ReadLine();
             }
             else
             {
                 string currentLine = reader.ReadLine();
                 processed += currentLine + Environment.NewLine;
             }
         }
         StreamWriter writer = new StreamWriter(file);
         writer.Write(processed);
    
         reader.Close();
         writer.Close();
         file.Close();
    });  
    

    你能告诉我怎么修吗?这段代码只是一个例子。

    我想使用字符串数组&在Parallel.ForEach中列出,但添加或编辑这些集合总是有问题。你能举个例子吗?我使用的是Visual Studio 2010+.NET Framework 4.0

    4 回复  |  直到 12 年前
        1
  •  7
  •   Reed Copsey    12 年前

    在编写的代码中,每个线程 使用相同的文件 ,并有效地尝试附加到它。即使这可以工作,你也会有一个糟糕的竞争条件(因为线程会试图同时附加到同一个文件)。

    你看到的错误纯粹是因为你在每个循环迭代中使用了相同的文件,所以当你试图打开文件(在第一次迭代之后)时,它会出错,因为它是由不同的循环迭代打开的。

    此外,您永远不会使用循环变量( line_acc ),所以这里根本不需要循环。这可以在没有 Parallel.ForEach ,并且您得到了相同的结果,没有任何问题。

    话虽如此,如果这是示例代码,您会发现纯粹由文件I/O绑定的循环往往无法很好地并行化。实际使用的驱动器将成为限制因素,因此运行纯粹并行读写文件的代码通常会导致生成的代码运行得比顺序运行更慢,而不是更快。

    我想使用字符串数组&在Parallel.ForEach中列出,但添加或编辑这些集合总是有问题

    您“作为示例”显示的代码没有执行这些操作,因此很难看出您的问题可能发生在哪里。您可以写入数组或 List<T> 通过索引,但如果没有额外的同步(例如 lock ),作为 列表<T> 对于写入而言不是线程安全的。如果您正在尝试从收藏中读写,您可以考虑查看 System.Collections.Concurrent 命名空间,其中包含可以在中安全使用的线程安全集合 并行循环 循环。

        2
  •  2
  •   Community CDub    7 年前

    如前所述 in this question 以下为:

    你没有同步对索引的访问,这意味着你有竞争。这就是你出现错误的原因。为了便于说明,您可以通过使用Interlocked.Increment来避免竞争并保持这种特定的设计。

    private static void Func<T>(IEnumerable<T> docs)
    {
        int index = -1;
        Parallel.ForEach(
            docs, doc =>
            {
                int nextIndex = Interlocked.Increment(index);
                CreateFolderAndCopyFile(nextIndex);
            }
        );
    }
    

    然而,正如其他人所建议的那样,提供循环索引的ForEach的替代重载显然是解决这个特定问题的更干净的方案。

    但当你开始工作时,你会发现复制文件是IO绑定的,而不是处理器绑定的,我预测并行代码会比串行代码慢。

        3
  •  1
  •   Guish    10 年前

    使用 lock 对象围绕有问题的代码。。。。执行将等待锁被释放,并且永远不会有多个线程访问资源。。。。并行ForEach在这种情况下不会增加性能。下面是一个简单的例子:

    private Object fileLock = new Object();
    private void WriteLog(string line)
    {
        lock (fileLock)
        {
            string strNomLog = @".\MyFile.log";
            System.IO.File.AppendAllText(strNomLog, line);
        }
    }
    
        4
  •  0
  •   Peter Ritchie    12 年前

    为了消除文件使用中的错误(假设它正在使用中,因为另一个线程正在向它写入),您必须同步对该文件的访问。这通常意味着每个并行执行都在等待其他执行完成写入,从而破坏了并行运行的目的。