C#中的异步文件复制/移动

时间:2009-05-19 13:14:37

标签: c# asynchronous

在C#中异步进行文件复制/移动的正确方法是什么?

9 个答案:

答案 0 :(得分:44)

异步编程的想法是允许调用线程(假设它是一个线程池线程)返回到线程池,以便在异步IO完成时用于某些其他任务。在引擎盖下,调用上下文被填充到数据结构中,并且一个或多个IO完成线程监视等待完成的调用。当IO完成完成时,线程将调用回到恢复调用上下文的线程池。这种方式而不是100个线程阻塞只有完成线程和几个线程池线程大多闲置。

我能想到的最好的是:

public async Task CopyFileAsync(string sourcePath, string destinationPath)
{
  using (Stream source = File.Open(sourcePath))
  {
    using(Stream destination = File.Create(destinationPath))
    {
      await source.CopyToAsync(destination);
    }
  }
}

我还没有对此进行过广泛的性能测试。我有点担心,因为如果它很简单就已经存在于核心库中了。

等待我在幕后所做的事情。如果你想了解它是如何工作的,那么理解Jeff Richter的AsyncEnumerator可能会有所帮助。它们可能不是完全相同的线,但想法非常接近。如果您从“异步”方法查看调用堆栈,您将在其上看到MoveNext。

就移动而言,它不需要是异步的,如果它真的是“移动”而不是副本然后删除。 Move是对文件表的快速原子操作。如果您不尝试将文件移动到其他分区,它只会以这种方式工作。

答案 1 :(得分:24)

这是一个异步文件复制方法,它提供了我们正在按顺序读取和写入的操作系统提示,以便它可以预读取读取数据并为写入做好准备:

public static async Task CopyFileAsync(string sourceFile, string destinationFile)
{
    using (var sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan))
    using (var destinationStream = new FileStream(destinationFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, 4096, FileOptions.Asynchronous | FileOptions.SequentialScan))
        await sourceStream.CopyToAsync(destinationStream);
}

您也可以尝试缓冲区大小。这是4096字节。

答案 2 :(得分:10)

我通过@DrewNoakes稍微增强了代码(性能和取消):

  public static async Task CopyFileAsync(string sourceFile, string destinationFile, CancellationToken cancellationToken)
  {
     var fileOptions = FileOptions.Asynchronous | FileOptions.SequentialScan;
     var bufferSize = 4096;

     using (var sourceStream = 
           new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, fileOptions))

     using (var destinationStream = 
           new FileStream(destinationFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize, fileOptions))

        await sourceStream.CopyToAsync(destinationStream, bufferSize, cancellationToken)
                                   .ConfigureAwait(continueOnCapturedContext: false);
  }

答案 3 :(得分:6)

您可以使用异步委托

public class AsyncFileCopier
    {
        public delegate void FileCopyDelegate(string sourceFile, string destFile);

        public static void AsynFileCopy(string sourceFile, string destFile)
        {
            FileCopyDelegate del = new FileCopyDelegate(FileCopy);
            IAsyncResult result = del.BeginInvoke(sourceFile, destFile, CallBackAfterFileCopied, null);
        }

        public static void FileCopy(string sourceFile, string destFile)
        { 
            // Code to copy the file
        }

        public static void CallBackAfterFileCopied(IAsyncResult result)
        {
            // Code to be run after file copy is done
        }
    }

您可以将其命名为:

AsyncFileCopier.AsynFileCopy("abc.txt", "xyz.txt");

这个link告诉你asyn编码的不同技术

答案 4 :(得分:6)

虽然在某些情况下您需要避免Task.Run,但Task.Run(() => File.Move(source, dest)会有效。值得考虑的是,因为当文件只是在同一磁盘/卷中移动时,它几乎是瞬时操作,因为标题被更改但文件内容不会被移动。各种“纯”异步方法总是复制流,即使没有必要这样做,因此在实践中可能会慢得多。

答案 5 :(得分:5)

您可以按this文章建议:

public static void CopyStreamToStream(
    Stream source, Stream destination,
    Action<Stream, Stream, Exception> completed)
    {
        byte[] buffer = new byte[0x1000];
        AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(null);

        Action<Exception> done = e =>
        {
            if(completed != null) asyncOp.Post(delegate
                {
                    completed(source, destination, e);
                }, null);
        };

        AsyncCallback rc = null;
        rc = readResult =>
        {
            try
            {
                int read = source.EndRead(readResult);
                if(read > 0)
                {
                    destination.BeginWrite(buffer, 0, read, writeResult =>
                    {
                        try
                        {
                            destination.EndWrite(writeResult);
                            source.BeginRead(
                                buffer, 0, buffer.Length, rc, null);
                        }
                        catch(Exception exc) { done(exc); }
                    }, null);
                }
                else done(null);
            }
            catch(Exception exc) { done(exc); }
        };

        source.BeginRead(buffer, 0, buffer.Length, rc, null);

答案 6 :(得分:2)

AFAIK,没有用于复制文件的高级异步API。但是,您可以使用Stream.BeginRead/EndReadStream.BeginWrite/EndWrite API构建自己的API来完成该任务。或者,您可以使用此处答案中提到的BeginInvoke/EndInvoke方法,但您必须记住,它们不会阻止异步I / O.他们只是在一个单独的线程上执行任务。

答案 7 :(得分:-2)

正确的复制方式:使用单独的线程。

以下是您可能会如何(同步):

//.. [code]
doFileCopy();
// .. [more code]

以下是异步执行的方法:

// .. [code]
new System.Threading.Thread(doFileCopy).Start();
// .. [more code]

这是一种非常天真的做事方式。做得好,解决方案将包括一些事件/委托方法来报告文件副本的状态,并通知重要事件,如失败,完成等。

欢呼声, JRH

答案 8 :(得分:-2)

我建议.Net编程语言中提供的文件复制IO功能在任何情况下都是异步的。在我的程序中使用它来移动小文件后,似乎后续指令在实际文件复制完成之前开始执行。我在说可执行文件给Windows执行复制任务,然后立即返回执行下一条指令 - 而不是等待Windows完成。这迫使我在调用复制之后构建while循环,直到我可以确认复制完成为止。