2

我对 Parallel.ForEach 真的很困惑......它是如何工作的?
下面的代码有错误->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

4 回答 4

7

在您的代码中,如所写,每个线程都使用相同的文件,并有效地尝试附加到它。即使这可行,您也会遇到糟糕的竞争条件(因为线程会尝试同时附加到同一个文件)。

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

此外,您从不使用循环变量 ( line_acc),因此这里根本不需要循环。这可以在没有 的情况下编写Parallel.ForEach,并且您有相同的结果,没有问题。

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

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

您“作为示例”显示的代码没有执行此操作,因此很难看出您的问题可能发生在哪里。您可以写入数组或List<T>按索引,但不能在没有额外同步(例如 a lock)的情况下在并行循环中添加到列表,因为List<T>写入不是线程安全的。如果您尝试从集合中读取和写入,您可能会考虑查看命名空间,其中包含您可以在循环System.Collections.Concurrent中安全使用的线程安全集合。Parallel.ForEach

于 2012-09-24T18:32:37.197 回答
2

本问题所述:

您没有同步对索引的访问,这意味着您有一场比赛。这就是为什么你有错误。出于说明目的,您可以通过使用 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 绑定而不是处理器绑定,我预测并行代码会比串行代码慢。

于 2012-09-24T18:16:34.180 回答
1

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

private Object fileLock = new Object();
private void WriteLog(string line)
{
    lock (fileLock)
    {
        string strNomLog = @".\MyFile.log";
        System.IO.File.AppendAllText(strNomLog, line);
    }
}
于 2015-03-25T14:01:29.897 回答
0

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

于 2012-09-24T18:28:17.467 回答