我目前正在创建一个应用程序,允许我选择一个目录,并根据您选择的内容将其复制到最多4个不同的位置
当它复制主要用于锁定时,我希望得到在不同线程上运行的目录副本以阻止这种情况发生。最重要的是,我需要链接每个目录副本的进度条以显示传输的进度。
当涉及到c#时,我有点生疏,如果有人能指出我正确的方向,我会很喜欢它。我尝试使用await / async但它仍然只是在彼此之后不同时运行每个目录副本。
private void button1_Click(object sender, EventArgs e)
{
//
// This event handler was created by double-clicking the window in the designer.
// It runs on the program's startup routine.
//
Path.GetFileName(folderBrowserDialog1.SelectedPath);
folderBrowserDialog1.RootFolder = Environment.SpecialFolder.MyComputer;
folderBrowserDialog1.SelectedPath = @"C:\Delivery";
DialogResult result = folderBrowserDialog1.ShowDialog();
if (result == DialogResult.OK)
{
//
// The user selected a folder and pressed the OK button.
// We print the number of files found.
//
// string[] files = Directory.GetFiles(folderBrowserDialog1.SelectedPath);
// MessageBox.Show("Files found: " + files.Length.ToString(), "Message");
if (checkBox1.Checked == true)
{
DirectoryCopy(folderBrowserDialog1.SelectedPath, "C:\\temp\\1\\" + Path.GetFileName(folderBrowserDialog1.SelectedPath), true);
}
if (checkBox2.Checked == true)
{
DirectoryCopy(folderBrowserDialog1.SelectedPath, "C:\\temp\\2\\" + Path.GetFileName(folderBrowserDialog1.SelectedPath), true);
}
if (checkBox3.Checked == true)
{
DirectoryCopy(folderBrowserDialog1.SelectedPath, "C:\\temp\\3\\" + Path.GetFileName(folderBrowserDialog1.SelectedPath), true);
}
if (checkBox4.Checked == true)
{
DirectoryCopy(folderBrowserDialog1.SelectedPath, "C:\\temp\\4\\" + Path.GetFileName(folderBrowserDialog1.SelectedPath), true);
}
if (checkBox5.Checked == true)
{
DirectoryCopy(folderBrowserDialog1.SelectedPath, "C:\\temp\\5\\"+ Path.GetFileName(folderBrowserDialog1.SelectedPath), true);
}
MessageBox.Show("These folders have been successfully transfered");
}
}
答案 0 :(得分:0)
您的问题有几个问题:
如何使用async-await复制目录
每个使用async-await的函数都必须返回Task
而不是void
和Task<TResult>
而不是TResult
。有一个例外:异步事件处理程序。事件处理程序返回void。
MSDN about Asynchronous File I/O附带以下内容:
public class FileCopier
{
public System.IO.FileInfo SourceFile {get; set;}
public System.IO.DirectoryInfo DestinationFolder {get; set;}
public async Task CopyAsync()
{
// TODO: throw exceptions if SourceFile / DestinationFile null
// TODO: throw exception if SourceFile does not exist
string destinationFileName = Path.Combine(DestinationFoler.Name, SourceFile.Name);
// TODO: decide what to do if destinationFile already exists
// open source file for reading
using (Stream sourceStream = File.Open(SourceFile, FileMode.Open))
{
// create destination file write
using (Stream destinationStream = File.Open(DestinationFile, FileMode.CreateNew))
{
await CopyAsync(sourceStream, destinationStream);
}
}
public async Task CopyAsync(Stream Source, Stream Destination)
{
char[] buffer = new char[0x1000];
int numRead;
while ((numRead = await Source.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
await Destination.WriteAsync(buffer, 0, numRead);
}
}
}
}
你的目录复印机可能是这样的:
class FolderCopier
{
public System.IO.DirectoryInfo SourceFolder {get; set;}
public System.IO.DirectoryInfo DestinationFolder {get; set;}
public Task CopyAsync()
{
foreach (FileInfo sourceFile in SourceFolder.EnumerateFiles())
{
var fileCopier = new FileCopier()
{
Sourcefile = sourceFile,
DestinationFolder = this.DestinationFolder,
};
await fileCopier.CopyAsync();
}
}
}
最后你的事件处理程序:
private async void OnButtonDeploy_Clicked(object sender, ...)
{
DirectoryInfo sourceFolder = GetSourceFolder(...);
IEnumerable<DirectoryInfo> destinationFolders = GetDestinationFolders();
IEnumerable<DirectoryCopier> folderCopiers = destinationFolders
.Select(destinationFolder => new FolderCopier()
{
SourceFolder = sourceFolder,
DestinationFolder = destinationFolder,
});
// if you want to copy the folders one after another while still keeping your UI responsive:
foreach (var folderCopier in folderCopiers)
{
await folderCopier.CopyAsync();
}
// or if you want to start copying all:
List<Task> folderCopyTasks = new List<Task>();
foreach (var folderCopier in folderCopiers)
{
folderCopyTasks.Add(folderCopier.CopyAsync());
}
await Task.WhenAll(folderCopyTasks);
}
您的用户界面中的反馈
如果足以更新每个复制文件的进度条,请考虑在复制文件时让FolderCopier引发事件。
public class FileCopiedEventArgs
{
public string DestinationFolder {get; set;}
public int NrOfFilesCopied {get; set;}
public int NrOfFilesToCopy {get; set;}
}
class FolderCopier
{
public System.IO.DirectoryInfo SourceFolder {get; set;}
public System.IO.DirectoryInfo DestinationFolder {get; set;}
public Task CopyAsync()
{
List<FileInfo> filesToCopy = DestinationFolder.EnumerateFiles().ToList();
for (int i=0; i<filesToCopy.Count; ++i)
{
// copy one file as mentioned before
// notify listeners:
this.OnFileCopied(i, filesToCopy.Count);
}
public event EventHandler<FileCopyiedEventArgs> EventFileCopied;
public void OnFileCopied(int nrOfCopiedFiles, int filesToCopy)
{
var tmpEvent = this.EventFileCopied;
if (tmpEvent != null)
{
tmpEvent.Invoke(this, new FileCopiedEventArgs()
{
DestinationFolder = this.DestinationFolder.Name,
NrOfFilesCopied = nrOfCopiedFiles,
NrOfFilesToCopy = filesToCopy
});
}
// instead of checking for null you can use the null-coalescent operator:
this.EventFileCopied?.Invocke(this, new ...);
}
}
}
注册这些活动:
private async void OnButtonDeploy_Clicked(object sender, ...)
{
var folderCopier = new FolderCopier(...);
folderCopier.EventFileCopied += eventFileCopied;
}
private void EventFileCopied(object sender, ...)
{
// for you to solve: if one copier has already copied 10%
// and another copier only 2%, what value should the progress bar have?
}
文件复制期间的效率
您希望将每个源文件复制到多个位置。如果在单独的进程中执行此操作,则每个目标文件将读取一次源文件。读取源文件一次并将其写入所有目标文件似乎更有效。
class MultiFileCopier
{
public System.IO.FileInfo SourceFile {get; set;}
public IEnumerable<System.IO.FileInfo> DestinationFiles {get; set;}
public async Task CopyAsync(object sender, RoutedEventArgs e)
{
// open your source file as a streamreader
using (Stream sourceStream = File.Open(SourceFile, Open))
{
// open all destination files for writing:
IEnumerable<Stream> destinationStreams = this.OpenDestinationsForWriting();
await CopyFilesAsync(sourceStream, destinationStreams);
this.DisposeDestinationStreams(destinationStreams);
}
}
public async Task CopyAsync(Stream Source, IEnumerable<Stream> destinations)
{
char[] buffer = new char[0x1000];
int numRead;
while ((numRead = await Source.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
// write one after another:
foreach (var destination in destinations)
{
await Destination.WriteAsync(buffer, 0, numRead);
}
// or write to them all at once:
List<Task> writeTasks = new List<Task>();
foreach (var destination in destinations)
{
writeTasks.Add(Destination.WriteAsync(buffer, 0, numRead));
}
await Task.WhenAll(writeTasks);
}
}
}
}
在我的所有示例中,我使用纯async-await而不启动新线程。只涉及一个线程(或者更确切地说:一次只有一个线程)。这怎么能让你的过程更快?
在this interview Eric Lippert比较异步等待与必须准备晚餐的厨师团队(在文章中间的某处搜索async-await)。
在Eric Lippert的比喻中,如果厨师必须等待面包吐司,他就不会懒得做什么。相反,他环顾四周,看看他是否可以做其他事情。过了一会儿,面包烤了,他继续加工烤面包,或者是一群厨师:他的一个同事处理烤好的面包。
同样在async-await中。你的线程一次只能做一件事,只要他很忙,他就什么都做不了。在目录复制期间,您的线程有几次等待,即在读取源文件期间和写入目标文件期间。因此,如果您告诉您的线程执行其他操作而不是等待,则可能会加快此过程。
乍一看似乎async-await会帮助你:在等待写入第一个文件时,你的线程可能会开始读取你的第二个文件。唉,写入文件的设备可能与读取文件的设备相同,因此您的设备太忙,无法处理读取第二个文件的请求。因此,如果您使用async-await,我不确定您的进程是否会快得多。
同样的,如果你真的开始了几个线程。如果写入不同的设备,它只会更快。
如果您只想保持UI响应,请考虑使用BackGroundWorker类。更容易启动和停止,更容易报告进度。