【问题标题】:Asynchronous file and folder copy - C#异步文件和文件夹复制 - C#
【发布时间】:2013-12-19 18:48:13
【问题描述】:

我有一种方法可以将一个目录中的所有文件和文件夹复制到另一个目录中,并且它以递归方式工作。我的问题是它阻塞了主线程,我想让文件和文件夹的实际复制异步。我目前正在使用一个函数来异步复制文件,但它似乎不起作用。 代码如下:

private async void copyEverything(string source, string target)
    {
        // Variable to hold the attributes of a file
        FileAttributes attributes;

        // Get the subdirectories for the specified directory.
        DirectoryInfo dir = new DirectoryInfo(source);
        DirectoryInfo[] dirs = dir.GetDirectories();

        if (!dir.Exists)
        {
            throw new DirectoryNotFoundException(
                "Source directory does not exist or could not be found: " + source);
        }

        // If the destination directory doesn't exist, create it. 
        if (!Directory.Exists(target))
        {
            Directory.CreateDirectory(target);
        }

        // Loop through for all files in a directory
        foreach (string filename in Directory.EnumerateFiles(source))
        {
            if (!File.Exists(targetFolder + filename.Substring(filename.LastIndexOf('\\'))))
            {
                attributes = File.GetAttributes(filename);

                if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                {
                    //System.Windows.MessageBox.Show("File {" + filename + "} is READ ONLY");
                }
                else
                {
                    try
                    {
                        using (FileStream SourceStream = File.Open(filename, FileMode.Open))
                        {
                            using (FileStream DestinationStream = File.Create(target + filename.Substring(filename.LastIndexOf('\\'))))
                            {
                                await SourceStream.CopyToAsync(DestinationStream);
                                filesRemaining--;
                            }
                        }
                    }
                    catch (UnauthorizedAccessException)
                    {
                    }
                }
            }
        }

        // Loop through each subdirectory in the current directory and recursively call the
        // copyEverything() method using the subdirectory's full name and the name of the
        // target folder plus the subdirectory folder name
        foreach (DirectoryInfo subdir in dirs)
        {
            foldersRemaining--;
            string temppath = System.IO.Path.Combine(target, subdir.Name);
            copyEverything(subdir.FullName, temppath);
        }
    }

有什么办法可以让它在不阻塞主线程的情况下工作吗?

【问题讨论】:

  • 旁注,您可以通过让EnumerateDirectoriesEnumerateFiles 使用搜索选项进行递归搜索,而不是使方法递归,从而大大提高性能。异步方法的递归比传统方法要昂贵得多。由于您似乎根本不需要在方法中使用 UI 上下文,您还可以在等待的任务上添加 ConfigureAwait(false)
  • 对于每个递归文件/文件夹副本,都会有一个文件系统会溢出您的堆栈。
  • 我很想分叉另一个进程来运行robocopy

标签: c# multithreading asynchronous file-io copy


【解决方案1】:

你仍然在主线程上做一堆IODirectory.ExistFile.Exist,等等...你可能想避免在主线程上做整个事情。

因此,一个简单的解决方案是添加一个新方法:

private void copyEverythingAsync(string source, string target)
{
  Task.Run(()=> copyEverything(source, target));
}

然后从copyEverything 方法中删除async/await

这会将操作从ThreadPool 移到一个新线程上,并且不会阻塞您的主线程。

【讨论】:

  • 但是他正在等待长时间运行的操作,所以它应该已经是适当的异步了。这只是隐藏问题,不管它是什么,而不是解决它。
  • @Servy 但是,他的问题是他不想阻塞主线程。
  • 您应该返回Task 以添加async/await 支持。
  • @Andrew 但它仍然不是 CPU 绑定的工作,它是 IO 工作,正如你所说,所以应该简单地进行异步处理,而不是卸载到线程池线程。
  • @Andrew 您建议将 IO 绑定工作卸载到线程池线程,这不适合异步程序。它隐藏了问题而不修复它,这是有害的。
【解决方案2】:

仅仅标记一个方法async 并不能使它成为async,你需要await 非UI/阻塞代码。 await 是你的异步代码应该去的地方,通常在 Task 中。

所有不在await 中的代码都将在调用线程的上下文中运行(在您的情况下是主线程)。因此,即使您是 await 复制副本,您也应该只使用 await 整个块,使用已经指定的 Task

【讨论】:

  • 他的代码正在等待。也许没有达到应有的程度,但等待最重要的一项操作。
  • 哎呀...错过了....我仍然认为他阻止的次数超出了应有的范围...我将编辑我的答案
  • 我确实有一个用于 CopyToAsync 方法的等待语句。我还有什么需要做的吗?
猜你喜欢
  • 2015-08-19
  • 1970-01-01
  • 2017-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多