将(多个)文件复制到多个位置
Copy (multiple) files to multiple locations
使用 C# (.NET 4.5) 我想将一组文件复制到多个位置(例如,将文件夹的内容复制到连接到计算机的 2 个 USB 驱动器)。
有没有比使用 foreach 循环和 File.Copy
?
更有效的方法呢?
正在努力寻找(可能的)解决方案。
我的第一个想法是某种多线程方法。经过一些阅读和研究后,我发现就 IO 而言,只是盲目地设置某种并行 and/or 异步进程并不是一个好主意(根据 Why is Parallel.ForEach much faster then AsParallel().ForAll() even though MSDN suggests otherwise?)。
瓶颈是磁盘,特别是如果它是传统驱动器,因为它只能 read/write 同步。这让我想到,如果我只读一次然后在多个位置输出它会怎么样?毕竟,在我的 USB 驱动器场景中,我正在处理多个(输出)磁盘。
虽然我不知道该怎么做。我认为我看到的一个 (Copy same file from multiple threads to multiple destinations) 是将每个文件的所有字节读入内存,然后循环遍历目标并将字节写出到每个位置,然后再移动到下一个文件。如果文件可能很大,这似乎是个坏主意。我将要复制的一些文件是视频,可能有 1 GB(或更多)。我无法想象将 1 GB 的文件加载到内存中只是为了将其复制到另一个磁盘是个好主意吗?
因此,考虑到较大文件的灵活性,我得到的最接近的文件如下(基于 How to copy one file to many locations simultaneously)。这段代码的问题是我仍然没有发生单读和多写。目前是多读多写。有没有办法进一步优化这段代码?我可以将块读入内存,然后将该块写入每个目的地,然后再移动到下一个块(就像上面的想法,但分块文件而不是整个文件)?
files.ForEach(fileDetail =>
Parallel.ForEach(fileDetail.DestinationPaths, new ParallelOptions(),
destinationPath =>
{
using (var source = new FileStream(fileDetail.SourcePath, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var destination = new FileStream(destinationPath, FileMode.Create))
{
var buffer = new byte[1024];
int read;
while ((read = source.Read(buffer, 0, buffer.Length)) > 0)
{
destination.Write(buffer, 0, read);
}
}
}));
IO 操作通常应被视为 asynchronous
,因为有些硬件操作 运行 在您的代码之外,因此您可以尝试为 read/write操作,所以你可以在硬件操作期间继续执行。
while ((read = await source.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await destination.WriteAsync(buffer, 0, read);
}
您还必须将您的 lambda 委托标记为 async
才能使此工作正常进行:
async destinationPath =>
...
而且你应该一直等待结果任务。您可以在这里找到更多信息:
Parallel foreach with asynchronous lambda
Nesting await in Parallel.ForEach
我想我会 post 我目前的解决方案,供遇到此问题的其他人使用。
如果有人发现更多 efficient/quicker 的方法,请告诉我!
我的代码似乎比 运行 同步复制文件要快一点,但它仍然没有我想要的那么快(也没有我看到的其他程序那么快) .我应该注意,性能可能会因 .NET 版本和您的系统而异(我在 13" MBP 和 2.9GHz i5(5287U - 2 核/4 线程)+ 16GB RAM 上使用 Win 10 和 .NET 4.5.2) .我什至还没有想出方法(例如FileStream.Write
、FileStream.WriteAsync
、BinaryWriter.Write
)和缓冲区大小的最佳组合。
foreach (var fileDetail in files)
{
foreach (var destinationPath in fileDetail.DestinationPaths)
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
// Set up progress
FileCopyEntryProgress progress = new FileCopyEntryProgress(fileDetail);
// Set up the source and outputs
using (var source = new FileStream(fileDetail.SourcePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, FileOptions.SequentialScan))
using (var outputs = new CompositeDisposable(fileDetail.DestinationPaths.Select(p => new FileStream(p, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize))))
{
// Set up the copy operation
var buffer = new byte[bufferSize];
int read;
// Read the file
while ((read = source.Read(buffer, 0, buffer.Length)) > 0)
{
// Copy to each drive
await Task.WhenAll(outputs.Select(async destination => await ((FileStream)destination).WriteAsync(buffer, 0, read)));
// Report progress
if (onDriveCopyFile != null)
{
progress.BytesCopied = read;
progress.TotalBytesCopied += read;
onDriveCopyFile.Report(progress);
}
}
}
if (ct.IsCancellationRequested)
break;
}
我正在使用 Reactive Extensions (https://github.com/Reactive-Extensions/Rx.NET) 中的 CompositeDisposable
。
使用 C# (.NET 4.5) 我想将一组文件复制到多个位置(例如,将文件夹的内容复制到连接到计算机的 2 个 USB 驱动器)。
有没有比使用 foreach 循环和 File.Copy
?
正在努力寻找(可能的)解决方案。
我的第一个想法是某种多线程方法。经过一些阅读和研究后,我发现就 IO 而言,只是盲目地设置某种并行 and/or 异步进程并不是一个好主意(根据 Why is Parallel.ForEach much faster then AsParallel().ForAll() even though MSDN suggests otherwise?)。
瓶颈是磁盘,特别是如果它是传统驱动器,因为它只能 read/write 同步。这让我想到,如果我只读一次然后在多个位置输出它会怎么样?毕竟,在我的 USB 驱动器场景中,我正在处理多个(输出)磁盘。
虽然我不知道该怎么做。我认为我看到的一个 (Copy same file from multiple threads to multiple destinations) 是将每个文件的所有字节读入内存,然后循环遍历目标并将字节写出到每个位置,然后再移动到下一个文件。如果文件可能很大,这似乎是个坏主意。我将要复制的一些文件是视频,可能有 1 GB(或更多)。我无法想象将 1 GB 的文件加载到内存中只是为了将其复制到另一个磁盘是个好主意吗?
因此,考虑到较大文件的灵活性,我得到的最接近的文件如下(基于 How to copy one file to many locations simultaneously)。这段代码的问题是我仍然没有发生单读和多写。目前是多读多写。有没有办法进一步优化这段代码?我可以将块读入内存,然后将该块写入每个目的地,然后再移动到下一个块(就像上面的想法,但分块文件而不是整个文件)?
files.ForEach(fileDetail =>
Parallel.ForEach(fileDetail.DestinationPaths, new ParallelOptions(),
destinationPath =>
{
using (var source = new FileStream(fileDetail.SourcePath, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var destination = new FileStream(destinationPath, FileMode.Create))
{
var buffer = new byte[1024];
int read;
while ((read = source.Read(buffer, 0, buffer.Length)) > 0)
{
destination.Write(buffer, 0, read);
}
}
}));
IO 操作通常应被视为 asynchronous
,因为有些硬件操作 运行 在您的代码之外,因此您可以尝试为 read/write操作,所以你可以在硬件操作期间继续执行。
while ((read = await source.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await destination.WriteAsync(buffer, 0, read);
}
您还必须将您的 lambda 委托标记为 async
才能使此工作正常进行:
async destinationPath =>
...
而且你应该一直等待结果任务。您可以在这里找到更多信息:
Parallel foreach with asynchronous lambda
Nesting await in Parallel.ForEach
我想我会 post 我目前的解决方案,供遇到此问题的其他人使用。
如果有人发现更多 efficient/quicker 的方法,请告诉我!
我的代码似乎比 运行 同步复制文件要快一点,但它仍然没有我想要的那么快(也没有我看到的其他程序那么快) .我应该注意,性能可能会因 .NET 版本和您的系统而异(我在 13" MBP 和 2.9GHz i5(5287U - 2 核/4 线程)+ 16GB RAM 上使用 Win 10 和 .NET 4.5.2) .我什至还没有想出方法(例如FileStream.Write
、FileStream.WriteAsync
、BinaryWriter.Write
)和缓冲区大小的最佳组合。
foreach (var fileDetail in files)
{
foreach (var destinationPath in fileDetail.DestinationPaths)
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
// Set up progress
FileCopyEntryProgress progress = new FileCopyEntryProgress(fileDetail);
// Set up the source and outputs
using (var source = new FileStream(fileDetail.SourcePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, FileOptions.SequentialScan))
using (var outputs = new CompositeDisposable(fileDetail.DestinationPaths.Select(p => new FileStream(p, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize))))
{
// Set up the copy operation
var buffer = new byte[bufferSize];
int read;
// Read the file
while ((read = source.Read(buffer, 0, buffer.Length)) > 0)
{
// Copy to each drive
await Task.WhenAll(outputs.Select(async destination => await ((FileStream)destination).WriteAsync(buffer, 0, read)));
// Report progress
if (onDriveCopyFile != null)
{
progress.BytesCopied = read;
progress.TotalBytesCopied += read;
onDriveCopyFile.Report(progress);
}
}
}
if (ct.IsCancellationRequested)
break;
}
我正在使用 Reactive Extensions (https://github.com/Reactive-Extensions/Rx.NET) 中的 CompositeDisposable
。