C#复制大量数据的方法
Method for copying large amounts of data in C#
我正在使用以下方法将目录的内容复制到不同的目录。
public void DirCopy(string SourcePath, string DestinationPath)
{
if (Directory.Exists(DestinationPath))
{
System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(DestinationPath);
foreach (FileInfo file in downloadedMessageInfo.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories())
{
dir.Delete(true);
}
}
//=================================================================================
string[] directories = System.IO.Directory.GetDirectories(SourcePath, "*.*", SearchOption.AllDirectories);
Parallel.ForEach(directories, dirPath =>
{
Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath));
});
string[] files = System.IO.Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories);
Parallel.ForEach(files, newPath =>
{
File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true);
});
}
我唯一的问题是源路径中有相当多的数据,并且在进行复制时程序变得无响应。
我想知道复制数据的选项是什么。我做了一些研究,有人建议使用缓冲区。
我还没有真正看到任何我理解得特别好的解决方案,所以任何help/resources清晰简洁的都很好。
感谢您的帮助!
如果您的目标是阻止应用程序进入 non-responsive 状态,建议的使用缓冲区的方法将无法解决您的问题。相反,请考虑使用单独的 Thread
来复制您的目录。
更好的是,使用 BackgroundWorker
,它具有能够报告进度的额外好处。
快速解决您的问题的方法是在调用代码中使用后台线程,如下所示:
var source_directory = "c:\source";
var destination_directory= "c:\destination";
Task.Run(() => DirCopy(source_directory, destination_directory));
此示例使用 Task.Run
method,它使用 thread-pool 线程之一来执行代码。
这确保 UI 线程可以自由更新 UI 并响应用户输入。
假设真正的问题是您的程序变得 non-responsive...
您的程序可能会停止响应,因为您用来执行复制的线程是您用来响应用户输入的线程。如果您希望在程序保持响应的同时在后台继续复制,则必须异步执行复制。 (我假设您根据上下文使用 winforms 或 wpf。)
典型的方法是简单地启动一个后台工作程序,将复制工作交给它,然后让它去镇上,而你的 gui 线程响应用户输入。还有其他更复杂的技术也有更好的权衡,但我怀疑根据您所描述的内容,这足以满足您的场景。
(您的 Parallel.ForEach
没有成功,因为触发它的线程在 Parallel.ForEach
完成执行之前不会继续)
在 Windows 表单中执行长任务,在消息线程上将导致表单无响应,直到任务完成。您将需要使用线程来防止这种情况发生。它可能会变得复杂,但您将需要一个 BackgroundWorker:
_Worker = new BackgroundWorker();
_Worker.WorkerReportsProgress = true;
_Worker.DoWork += Worker_DoWork;
_Worker.ProgressChanged += Worker_ProgressChanged;
_Worker.RunWorkerAsync();
执行任务的方法:
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
worker.ReportProgress(1);
// do stuff
worker.ReportProgress(100);
}
以及报告进度的方法:
private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
switch (e.ProgressPercentage)
{
case 1:
// make your status bar visible
break;
case 100:
// hide it again
break;
}
}
您可以使用选取框进度条,但如果您想返回实际百分比,计算文件大小和 Worker_DoWork 方法中的进度可能会变得复杂并且是另一个问题。
https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx
不清楚您使用的是哪个版本的编译器/框架,但您可以使用异步文件操作而不必担心线程。如果您的文件层次结构很大,您还可以从使用流媒体版本 EnumerateDirectories
和 EnumerateFiles
中受益。
public async Task DirCopy(string SourcePath, string DestinationPath)
{
//slightly different from your code, in that the destination directory is simply removed recursively
Directory.Delete(DestinationPath, true);
//enumerate files returns earlier than get files for large file hierarchies
//... because it is a streaming IEnumerable instead of an array
foreach (var sourcePath in System.IO.Directory.EnumerateFiles(SourcePath, "*.*", SearchOption.AllDirectories))
{
var destinationPath = sourcePath.Replace(SourcePath, DestinationPath);
//slightly different from your code, in that directories are created as needed
//... however, this would mean empty directories are not copied
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
using (var source = File.Open(sourcePath, FileMode.Open, FileAccess.Read))
using (var destination = File.Create(destinationPath))
{
//async copy of the file frees the current thread
//... e.g. for the UI thread to process UI updates
await source.CopyToAsync(destination);
}
}
}
谢谢大家,我非常感谢所有的输入,以了解实现此目标的不同方法。目前我决定只做一个 Task.Run 但我将研究后台工作人员和异步操作。
再次感谢大家!
我刚做的参考
Task.Run(()=>{ DirCopy("source","destination"); });
目录复制
public void DirCopy(string SourcePath, string DestinationPath)
{
if (Directory.Exists(DestinationPath))
{
System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(DestinationPath);
foreach (FileInfo file in downloadedMessageInfo.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories())
{
dir.Delete(true);
}
}
//=================================================================================
string[] directories = System.IO.Directory.GetDirectories(SourcePath, "*.*", SearchOption.AllDirectories);
string[] files = System.IO.Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories);
totalPB.Minimum = 0;
totalPB.Maximum = directories.Length;
totalPB.Value = 0;
totalPB.Step = 1;
subTotalPB.Minimum = 0;
subTotalPB.Maximum = directories.Length;
subTotalPB.Value = 0;
subTotalPB.Step = 1;
Parallel.ForEach(directories, dirPath =>
{
Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath));
subTotalPB.PerformStep();
});
Task.Run(() =>
{
Parallel.ForEach(files, newPath =>
{
File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true);
totalPB.PerformStep();
});
});
}
我正在使用以下方法将目录的内容复制到不同的目录。
public void DirCopy(string SourcePath, string DestinationPath)
{
if (Directory.Exists(DestinationPath))
{
System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(DestinationPath);
foreach (FileInfo file in downloadedMessageInfo.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories())
{
dir.Delete(true);
}
}
//=================================================================================
string[] directories = System.IO.Directory.GetDirectories(SourcePath, "*.*", SearchOption.AllDirectories);
Parallel.ForEach(directories, dirPath =>
{
Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath));
});
string[] files = System.IO.Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories);
Parallel.ForEach(files, newPath =>
{
File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true);
});
}
我唯一的问题是源路径中有相当多的数据,并且在进行复制时程序变得无响应。
我想知道复制数据的选项是什么。我做了一些研究,有人建议使用缓冲区。
我还没有真正看到任何我理解得特别好的解决方案,所以任何help/resources清晰简洁的都很好。
感谢您的帮助!
如果您的目标是阻止应用程序进入 non-responsive 状态,建议的使用缓冲区的方法将无法解决您的问题。相反,请考虑使用单独的 Thread
来复制您的目录。
更好的是,使用 BackgroundWorker
,它具有能够报告进度的额外好处。
快速解决您的问题的方法是在调用代码中使用后台线程,如下所示:
var source_directory = "c:\source";
var destination_directory= "c:\destination";
Task.Run(() => DirCopy(source_directory, destination_directory));
此示例使用 Task.Run
method,它使用 thread-pool 线程之一来执行代码。
这确保 UI 线程可以自由更新 UI 并响应用户输入。
假设真正的问题是您的程序变得 non-responsive...
您的程序可能会停止响应,因为您用来执行复制的线程是您用来响应用户输入的线程。如果您希望在程序保持响应的同时在后台继续复制,则必须异步执行复制。 (我假设您根据上下文使用 winforms 或 wpf。)
典型的方法是简单地启动一个后台工作程序,将复制工作交给它,然后让它去镇上,而你的 gui 线程响应用户输入。还有其他更复杂的技术也有更好的权衡,但我怀疑根据您所描述的内容,这足以满足您的场景。
(您的 Parallel.ForEach
没有成功,因为触发它的线程在 Parallel.ForEach
完成执行之前不会继续)
在 Windows 表单中执行长任务,在消息线程上将导致表单无响应,直到任务完成。您将需要使用线程来防止这种情况发生。它可能会变得复杂,但您将需要一个 BackgroundWorker:
_Worker = new BackgroundWorker();
_Worker.WorkerReportsProgress = true;
_Worker.DoWork += Worker_DoWork;
_Worker.ProgressChanged += Worker_ProgressChanged;
_Worker.RunWorkerAsync();
执行任务的方法:
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
worker.ReportProgress(1);
// do stuff
worker.ReportProgress(100);
}
以及报告进度的方法:
private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
switch (e.ProgressPercentage)
{
case 1:
// make your status bar visible
break;
case 100:
// hide it again
break;
}
}
您可以使用选取框进度条,但如果您想返回实际百分比,计算文件大小和 Worker_DoWork 方法中的进度可能会变得复杂并且是另一个问题。
https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx
不清楚您使用的是哪个版本的编译器/框架,但您可以使用异步文件操作而不必担心线程。如果您的文件层次结构很大,您还可以从使用流媒体版本 EnumerateDirectories
和 EnumerateFiles
中受益。
public async Task DirCopy(string SourcePath, string DestinationPath)
{
//slightly different from your code, in that the destination directory is simply removed recursively
Directory.Delete(DestinationPath, true);
//enumerate files returns earlier than get files for large file hierarchies
//... because it is a streaming IEnumerable instead of an array
foreach (var sourcePath in System.IO.Directory.EnumerateFiles(SourcePath, "*.*", SearchOption.AllDirectories))
{
var destinationPath = sourcePath.Replace(SourcePath, DestinationPath);
//slightly different from your code, in that directories are created as needed
//... however, this would mean empty directories are not copied
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
using (var source = File.Open(sourcePath, FileMode.Open, FileAccess.Read))
using (var destination = File.Create(destinationPath))
{
//async copy of the file frees the current thread
//... e.g. for the UI thread to process UI updates
await source.CopyToAsync(destination);
}
}
}
谢谢大家,我非常感谢所有的输入,以了解实现此目标的不同方法。目前我决定只做一个 Task.Run 但我将研究后台工作人员和异步操作。
再次感谢大家!
我刚做的参考
Task.Run(()=>{ DirCopy("source","destination"); });
目录复制
public void DirCopy(string SourcePath, string DestinationPath)
{
if (Directory.Exists(DestinationPath))
{
System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(DestinationPath);
foreach (FileInfo file in downloadedMessageInfo.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories())
{
dir.Delete(true);
}
}
//=================================================================================
string[] directories = System.IO.Directory.GetDirectories(SourcePath, "*.*", SearchOption.AllDirectories);
string[] files = System.IO.Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories);
totalPB.Minimum = 0;
totalPB.Maximum = directories.Length;
totalPB.Value = 0;
totalPB.Step = 1;
subTotalPB.Minimum = 0;
subTotalPB.Maximum = directories.Length;
subTotalPB.Value = 0;
subTotalPB.Step = 1;
Parallel.ForEach(directories, dirPath =>
{
Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath));
subTotalPB.PerformStep();
});
Task.Run(() =>
{
Parallel.ForEach(files, newPath =>
{
File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true);
totalPB.PerformStep();
});
});
}