当多个线程生成的进程将文件复制到同一目录 C# 时缺少文件条目
Missing file entries when multiple threads-generated processes copy files to same directory C#
我正在尝试将大量文件从不同主机上的不同目录复制到单个目录。由于结果证明它非常耗时,而且我不得不多次运行这个程序,所以我更改了代码以利用多线程的帮助,每个线程通过一个进程进行XCOPY。
但是,我发现在进行多线程复制时,有些文件(少于100 个,每次都不同)没有被复制。我还没有弄清楚是因为在对同一个目录进行 XCOPY 时多个进程相互干预,还是更多的是 XCOPY 问题(不确定它在同一目标上支持多进程调用的程度如何)。
List<Thread> threadList = new List<Thread>;
foreach(FileEntry fileEntry in fileEntries)
{
Thread thread = new Thread(() => {
//full path of file, like \host\directory\directoryB\fileA.txt
string filePath = fileEntry._filePath;
//name of file, like fileA.txt
string file = fileEntry._file;
//dumpDirectory is where I want to copy all the files to
string destPath = Path.Combine(dumpDirectory, file);
//each file here is either a directory or a real file, bad naming convention I know...(open to suggestions if any)
string fileType = File.Exists(filePath) ? "f" : "d";
using (Process process = new Process())
{
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = string.Format(@"/C echo {0} | XCOPY /E /C /H /R /K /O /Y {1} {2}", fileType, filePath, destPath);
Console.WriteLine("copying {2} [{0}] to destination [{1}]", filePath, destPath, string.Compare(fileType, "f") == 0 ? "file" : "directory");
process.Start();
process.WaitForExit();
if (process.ExitCode != 0)
{
Console.WriteLine("encountered problems when copying file [{0}]", filePath);
}
}
});
thread.Start();
threadList.Add(thread);
}
foreach(Thread thread in threadList)
{
thread.Join();
}
-------------------------错误修复如下------------ ------------------
所以按照下面的回复建议(感谢大家的快速回复,节省了我的周末^_^),我重定向了流程输出并发现问题是"Sharing violation \n Unable to create directory "。 xcopy 在将多个文件复制到同一目录时出现问题(各种 xcopy 进程似乎同时创建一个目录,但它们都检测到该目录在系统上不存在)。
将手动线程管理更改为 Parallel.Foreach 解决了问题并使代码看起来更好(虽然我还没有弄清楚为什么它没有产生同样的问题)
或者,更脏的解决方法是用 EventWaitHandle 包装 process.Start()。我根据目标目录使用了不同的 EventWaitHandle 实例,而不是使用一个句柄,因为另一种方式会破坏使用多进程的目的
//name the wait handle based on destination value
EventWaitHandle destWaitHandle = new EventWaitHandle(true, EventResetMode.AutoReset, string.Format("waitHandle{0}", destPath.GetHashCode()));
//only trigger the wait handle lock if the file being copied from (and to) is a directory. Based on the error log info XCopy seemed to never have issues with copying individual files
//and the program worked fine if I only put the lock at directory level
if (string.Compare(fileType, "d") == 0)
{
destWaitHandle.WaitOne();
}
process.Start();
//this line was added to write error messages into respective files during the process' execution
process.BeginErrorReadLine();
process.WaitForExit();
if (string.Compare(fileType, "d") == 0)
{
destWaitHandle.Set();
}
首先,我建议你切换到面向任务的代码,因为在这样数量的线程上创建可能会因为上下文切换而导致性能下降。
您可以使用并行扩展轻松做到这一点,如下所示:
Parallel.ForEach(fileEntries, fileEntry => { // YOUR code here };
其次,您可以通过检查 Process
对象的 StandardError
属性 来调查 XCOPY
过程中发生的错误,如@x...所建议的, something like this(您还必须重定向它才能阅读它):
process.StartInfo.RedirectStandardError = true;
// ...
if (process.ExitCode != 0)
{
Console.WriteLine("encountered problems when copying file [{0}]", filePath);
Console.WriteLine(process.StandardError.ReadToEnd());
}
我正在尝试将大量文件从不同主机上的不同目录复制到单个目录。由于结果证明它非常耗时,而且我不得不多次运行这个程序,所以我更改了代码以利用多线程的帮助,每个线程通过一个进程进行XCOPY。
但是,我发现在进行多线程复制时,有些文件(少于100 个,每次都不同)没有被复制。我还没有弄清楚是因为在对同一个目录进行 XCOPY 时多个进程相互干预,还是更多的是 XCOPY 问题(不确定它在同一目标上支持多进程调用的程度如何)。
List<Thread> threadList = new List<Thread>;
foreach(FileEntry fileEntry in fileEntries)
{
Thread thread = new Thread(() => {
//full path of file, like \host\directory\directoryB\fileA.txt
string filePath = fileEntry._filePath;
//name of file, like fileA.txt
string file = fileEntry._file;
//dumpDirectory is where I want to copy all the files to
string destPath = Path.Combine(dumpDirectory, file);
//each file here is either a directory or a real file, bad naming convention I know...(open to suggestions if any)
string fileType = File.Exists(filePath) ? "f" : "d";
using (Process process = new Process())
{
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = string.Format(@"/C echo {0} | XCOPY /E /C /H /R /K /O /Y {1} {2}", fileType, filePath, destPath);
Console.WriteLine("copying {2} [{0}] to destination [{1}]", filePath, destPath, string.Compare(fileType, "f") == 0 ? "file" : "directory");
process.Start();
process.WaitForExit();
if (process.ExitCode != 0)
{
Console.WriteLine("encountered problems when copying file [{0}]", filePath);
}
}
});
thread.Start();
threadList.Add(thread);
}
foreach(Thread thread in threadList)
{
thread.Join();
}
-------------------------错误修复如下------------ ------------------
所以按照下面的回复建议(感谢大家的快速回复,节省了我的周末^_^),我重定向了流程输出并发现问题是"Sharing violation \n Unable to create directory "。 xcopy 在将多个文件复制到同一目录时出现问题(各种 xcopy 进程似乎同时创建一个目录,但它们都检测到该目录在系统上不存在)。
将手动线程管理更改为 Parallel.Foreach 解决了问题并使代码看起来更好(虽然我还没有弄清楚为什么它没有产生同样的问题)
或者,更脏的解决方法是用 EventWaitHandle 包装 process.Start()。我根据目标目录使用了不同的 EventWaitHandle 实例,而不是使用一个句柄,因为另一种方式会破坏使用多进程的目的
//name the wait handle based on destination value
EventWaitHandle destWaitHandle = new EventWaitHandle(true, EventResetMode.AutoReset, string.Format("waitHandle{0}", destPath.GetHashCode()));
//only trigger the wait handle lock if the file being copied from (and to) is a directory. Based on the error log info XCopy seemed to never have issues with copying individual files
//and the program worked fine if I only put the lock at directory level
if (string.Compare(fileType, "d") == 0)
{
destWaitHandle.WaitOne();
}
process.Start();
//this line was added to write error messages into respective files during the process' execution
process.BeginErrorReadLine();
process.WaitForExit();
if (string.Compare(fileType, "d") == 0)
{
destWaitHandle.Set();
}
首先,我建议你切换到面向任务的代码,因为在这样数量的线程上创建可能会因为上下文切换而导致性能下降。
您可以使用并行扩展轻松做到这一点,如下所示:
Parallel.ForEach(fileEntries, fileEntry => { // YOUR code here };
其次,您可以通过检查 Process
对象的 StandardError
属性 来调查 XCOPY
过程中发生的错误,如@x...所建议的, something like this(您还必须重定向它才能阅读它):
process.StartInfo.RedirectStandardError = true;
// ...
if (process.ExitCode != 0)
{
Console.WriteLine("encountered problems when copying file [{0}]", filePath);
Console.WriteLine(process.StandardError.ReadToEnd());
}