C# 使用Parallel.for循环解决文件I/O问题
我有一个控制台应用程序,计划处理大量平面文件。为了提高性能,我提供了使用并行处理的选项。它极大地提高了性能。然而,当某些迭代复制和删除文件时,它现在会导致奇怪的错误。我不知道为什么会发生这种情况,也不知道如何解决。我不明白为什么迭代分配给的线程会冲突,因为每个文件和关联的ID都不同。以下是我的基本代码和错误:C# 使用Parallel.for循环解决文件I/O问题,c#,multithreading,file-io,parallel-processing,task-parallel-library,C#,Multithreading,File Io,Parallel Processing,Task Parallel Library,我有一个控制台应用程序,计划处理大量平面文件。为了提高性能,我提供了使用并行处理的选项。它极大地提高了性能。然而,当某些迭代复制和删除文件时,它现在会导致奇怪的错误。我不知道为什么会发生这种情况,也不知道如何解决。我不明白为什么迭代分配给的线程会冲突,因为每个文件和关联的ID都不同。以下是我的基本代码和错误: static void Main(string[] args) { Parallel.For(0, fileCount, i =>
static void Main(string[] args)
{
Parallel.For(0, fileCount, i =>
{
dxmtId = Convert.ToInt32(dxmtIds[i]);
iflId = Convert.ToInt32(iflIds[i]);
islId = Convert.ToInt32(islIds[i]);
fileName = fileNames[i].ToString();
LoadFileIntoDatabase(monitorId, islId, dxmtId, iflId, fileName);
});
}
private static void LoadFileIntoDatabase (int procId, int islId, int dxmtId, iflId, fileName )
{
string fileNameDone = fileName + ".done";
if (File.Exists(fileName))
{
// code for successfully loading file
myCommand = @"CMD.EXE";
ProcessStartInfo startInfo = new ProcessStartInfo(myCommand)
{
WorkingDirectory = ConfigurationManager.AppSettings["ExportPath"].ToString(),
Arguments = @"/c SQLLDR CONTROL=" + controlFileWithPath + " PARFILE=" + parFileWithPath,
//RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
exitCode = process.ExitCode;
try
{
File.Copy(fileName, fileNameDone, true); //rename file to .done
File.Delete(fileName); //delete original file
}
catch (exception ex)
{
File.AppendAllText(@"c:\temp\fileerrors.txt", ex.Message + " " + " on copying or deleting file name: " + fileName + Environment.NewLine);
}
}
}
错误为1)“找不到文件…”或2)“进程无法访问文件…”
关于如何修复/诊断正在发生的情况,有什么建议吗 我认为问题很可能是
File.Copy()
的过程仍然对原始文件有句柄,因此File.Delete()
失败了
我建议您改用File.Move()
,因为这实际上不太占用资源。在内部,File.Move()
使用Win32Native.MoveFile
函数对文件系统进行重命名。如果使用File.Copy()
,实际上是在复制磁盘上的数据,这样会占用更多的资源,速度也会更慢。但是,如果需要保留数据的两个副本(在您的示例中似乎并非如此),则应避免使用File.Move()
您所需的代码更改看起来有点像这样:
try
{
File.Move(fileName, fileNameDone);
}
您可能还想更仔细地查看catch
块,更准确地定位已知错误,即
catch (IOException ex)
{
// specific error type expected
}
希望这能让您更进一步。在您的
参数中,controlFileWithPath
等有哪些值?可能需要引用文件路径,例如,如果路径中有空格,则执行命令cmd.exe/c SQLLDR CONTROL=“c:\temp\some file.txt”
。如果操作确实彼此独立,那么将它们并行化就可以了。但是,如果SQLLDR
程序试图并行写入数据库,这可能会再次给您带来一些问题。我认为问题很可能是File.Copy()
的进程可能仍然对原始文件有句柄,因此File.Delete()
失败,因为复制功能未等待完成。也许您应该使用File.Move()
来代替(单操作)??。只是一个inkling@MaximilianGerhardt:controlFileWithPath和parFileWithPath是临时元数据文件,其路径是我从另一个函数生成的,未在此代码中列出。它们返回不带空格的路径,例如“c:\\temp\\634b99e3-77a7-43aa-b9d7-c60735c13e48.ctl”,并且仅由sql loader用于加载文件。错误发生在File.Copy/File.Delete语句中,这些语句位于我加载的实际文件(而不是您询问的元数据文件)上。我将不得不研究sqlldr可能并行工作的想法。但我不认为这是默认行为。thanks@jimtollan我以前使用过.Move(),但现在我会再试一次,看看它是否会改变什么。很高兴知道您是否解决了此问题??File.Move正在执行相同的操作。有趣的是,当我查看在catch块中创建的错误文件时,在某些情况下,我看到同一文件的多个错误条目“找不到文件xxxx”,并且在我的输出目录中确实有一个.done文件。因此,它确实工作并移动了文件,但它再次尝试,显然失败了,因为文件不再存在。我检查了我的名单,没有重复的名字。似乎线程正在以某种方式绑定到处理同一个文件。我不明白!当我的应用程序尝试移动文件时,是否可以查看sql loader是否对该文件具有句柄?我有更多的信息,希望有人能正确引导我。1.每一个导致错误的File.Move()语句实际上都是有效的(我不知道该怎么做)。2.当我从代码中取出sql加载器处理时,没有错误。显然,这与启动sql loader的许多线程有关。但我仍然不知道,当文件名不同时,如何尝试两次相同的文件:process.Start();process.WaitForExit()//这是否会导致释放任何句柄?可能是因为fileerrors.txt在前一个错误上被锁定,而随后尝试写入时失败,所以实际发生了错误。看不到任何其他突出的问题我想我已经发现了问题所在。它在我们传递给sql loader的控制文件中。事实证明,这些文件中有多个重复的文件名,所以当并行运行时,可以想象有2个以上的线程试图同时处理同一个文件。我检查文件的存在性,处理它,然后在完成后重命名它,所以可能它后面的线程正在将它放入其中,再次加载它,并尝试在重新命名后重命名它。因此出现“找不到文件”和“拒绝访问”错误。而在连续剧中,自然没有问题。谢谢你的意见。