2009-05-19 49 views

回答

-1

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

下面是你可能会(同步)这样做:

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

这里是如何做到这一点异步:

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

这是做事情很天真的方式。如果做得好,该解决方案将包括一些事件/委托方法来报告的文件副本的状态,并通知喜欢失败,竣工等重要事件

欢呼声, JRH

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告诉你不同的TECHNI ASYN编码

+4

我认为问题是在不消耗线程的情况下异步执行操作。有多种方式将工作委托给线程池,其中大部分比这里的机制更容易。 – 2012-01-28 05:21:45

5

你可以做到这一点作为this文章QUES建议:

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); 
+0

流现在有一个内置的复制操作,使这更容易。但是我用这种技术的问题是它总是复制文件,即使它在同一个磁盘上,并且不需要这样的操作。 – Casey 2016-08-04 01:52:48

2

据我所知,没有高水平的异步API来复制文件。但是,您可以使用Stream.BeginRead/EndReadStream.BeginWrite/EndWrite API构建自己的API来完成该任务。或者,您可以使用BeginInvoke/EndInvoke方法,如答案中所述,但您必须记住,它们不会是非阻塞异步I/O。他们只是在一个单独的线程上执行任务。

-2

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

+4

工作原因是,如果您要在同一个驱动器内移动文件,除了标题外,不需要重新编写任何文件。如果你移动到另一个驱动器,你可以说服自己,这不是一个异步操作。 – Casey 2015-03-31 11:30:05

+0

为了扩大凯西的反应,通过VPN或WAN复制文件通常都很慢。 – 2015-04-23 22:24:58

32

异步编程的思想是允许调用线程(假设它是一个线程池线程)在异步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); 
    } 
    } 
} 

我没有做大量的测试PERF这个虽然。我有点担心,因为如果它那么简单,它已经在核心库中。

等待我在幕后描述。如果你想了解它的工作原理,这可能有助于理解Jeff Richter的AsyncEnumerator。他们可能并不完全一致,但创意非常接近。如果您从“异步”方法查看调用堆栈,您将看到MoveNext。

只要移动它不需要是异步如果它真的是一个“移动”,而不是一个副本,然后删除。移动是对文件表的快速原子操作。它只能以这种方式工作,尽管如果你不试图将文件移动到不同的分区。

+0

请你可以告诉我,这是什么意思(等待source.CopyToAsync(destination);)? – 2013-12-05 14:21:23

+2

内部在标记为异步的方法中,等待等待等待完成的代码拼接。天真地,我们可以说它阻止。 但它并没有真正阻止。像Wait()这样的真正的阻塞行为会使活动线程停留在执行点。等待实际上会导致线程正在做的任何事情的上下文被困在一个数据结构中,并允许活动线程返回到可用于其他事情的线程池。等待时返回一个线程池线程(可能不是同一个线程)检索上下文并恢复执行。 – csaam 2014-01-15 08:24:17

+0

这听起来不像是一件大事,但正确使用异步可能意味着减少运行的实际活动线程的数量。在io密集型服务中,这可能是一件大事。我编写的代码可以有80个活动并发请求,但只有5个左右的活动线程。 最终结果是CPU使用率更高,但每个实例的服务都有更高的吞吐量。所以,你的硬件更加昂贵。 – csaam 2014-01-15 08:29:52

16

这里是一个异步文件复制方法,让操作系统的提示,我们正在阅读和写作顺序,以便它可以在读预取数据,并有东西准备好写:

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字节。

8

我已经通过加强代码@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

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