C# File.Copy in Parallel.ForEach

C# File.Copy in Parallel.ForEach,c#,parallel-processing,file-copying,C#,Parallel Processing,File Copying,我正在尝试创建一个目录并在Parallel.ForEach中复制一个文件(pdf) 下面是一个简单的例子: private static void CreateFolderAndCopyFile(int index) { const string sourcePdfPath = "c:\\testdata\\test.pdf"; const string rootPath = "c:\\testdata"; string fold

我正在尝试创建一个目录并在
Parallel.ForEach
中复制一个文件(pdf)

下面是一个简单的例子:

    private static void CreateFolderAndCopyFile(int index)
    {
        const string sourcePdfPath = "c:\\testdata\\test.pdf";
        const string rootPath = "c:\\testdata";

        string folderDirName = string.Format("Data{0}", string.Format("{0:00000000}", index));

        string folderDirPath = rootPath + @"\" + folderDirName;

        Directory.CreateDirectory(folderDirPath);

        string desPdfPath = folderDirPath + @"\" + "test.pdf";

        File.Copy(sourcePdfPath, desPdfPath, true);

    }
上述方法创建一个新文件夹,并将pdf文件复制到一个新文件夹。 它将创建此目录树:

TESTDATA
  -Data00000000
      -test.pdf
  -Data00000001
      -test.pdf
....
  -Data0000000N
      -test.pdf
我尝试在
Parallel.ForEach
循环中调用
CreateFolderAndCopyFile
方法

    private static void Func<T>(IEnumerable<T> docs)
    {
        int index = 0;
        Parallel.ForEach(docs, doc =>
                                   {
                                       CreateFolderAndCopyFile(index);
                                       index++;
                                   });
    }
private静态无效函数(IEnumerable文档)
{
int指数=0;
Parallel.ForEach(docs,doc=>
{
CreateFolderAndCopyFile(索引);
索引++;
});
}
当我运行此代码时,它会以以下错误结束:

进程无法访问文件“c:\testdata\Data00001102\test.pdf” 因为它正被另一个进程使用

但首先它创建了1111个新文件夹,并在我出现这个错误之前复制了大约1111次test.pdf

是什么导致了这种行为?如何解决

编辑:

上面的代码是玩具示例,很抱歉硬编码字符串 结论:并行方法速度慢。

明天我会尝试一些方法


特别是:

您没有同步对
索引的访问,这意味着您在该索引上有竞争。这就是为什么你会有错误。为了便于说明,您可以通过使用
Interlocked.Increment
避免竞争并保持此特定设计

private static void Func<T>(IEnumerable<T> docs)
{
    int index = -1;
    Parallel.ForEach(
        docs, doc =>
        {
            int nextIndex = Interlocked.Increment(index);
            CreateFolderAndCopyFile(nextIndex);
        }
    );
}
private静态无效函数(IEnumerable文档)
{
int指数=-1;
并行ForEach(
文档,文档=>
{
int nextIndex=联锁增量(索引);
CreateFolderAndCopyFile(nextIndex);
}
);
}
然而,正如其他人所建议的,提供循环索引的替代重载显然是解决这个特定问题的更干净的解决方案


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

您对
索引的增量操作怀疑它不是线程安全的。如果将操作更改为
Console.WriteLine({0}),index++)
您将看到此行为

相反,您可以使用带有循环索引的重载:

private static void Func<T>(IEnumerable<T> docs)
{
    // nb: index is 'long' not 'int'
    Parallel.ForEach(docs, (doc, state, index) =>
                            {
                                CreateFolderAndCopyFile(index);
                            });
}
private静态无效函数(IEnumerable文档)
{
//注意:索引是“long”而不是“int”
Parallel.ForEach(文档,(文档、状态、索引)=>
{
CreateFolderAndCopyFile(索引);
});
}

作为旁白,您可能应该使用
路径。合并
而不是自己连接路径。@Mike我注意到您尚未在网站上投票或接受答案。我建议您阅读一下,以了解堆栈溢出社区的这些方面。这是我见过的最复杂的foreach。恭喜你@BrianGraham 2行ForEach很复杂?@AndrewFinnell我猜Brian指的是
索引
handling@AndrewFinnell
for(int i=0;i您不会比系统提供的文件复制例程做得更好。