C# C语言中的非阻塞文件复制#

C# C语言中的非阻塞文件复制#,c#,asynchronous,C#,Asynchronous,如何在不阻塞线程的情况下用C#复制文件?正确的复制方法:使用单独的线程 以下是您可能(同步)执行的方式: 以下是如何异步执行此操作: //.. [code] doFileCopy(); // .. [more code] // .. [code] new System.Threading.Thread(doFileCopy).Start(); // .. [more code] 这是一种非常幼稚的做事方式。如果做得好,解决方案将包括一些事件/委托方法来报告文件副本的状态,并通知重要事件,如失

如何在不阻塞线程的情况下用C#复制文件?

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

以下是您可能(同步)执行的方式:

以下是如何异步执行此操作:

//.. [code]
doFileCopy();
// .. [more code]
// .. [code]
new System.Threading.Thread(doFileCopy).Start();
// .. [more code]
这是一种非常幼稚的做事方式。如果做得好,解决方案将包括一些事件/委托方法来报告文件副本的状态,并通知重要事件,如失败、完成等

干杯,
jrh

您可以使用异步委托

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");
这告诉您asyn编码的不同技术

您可以按照文章的建议进行:

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);
公共静态无效CopyStreamToStream(
流源、流目的地、,
行动完成)
{
字节[]缓冲区=新字节[0x1000];
AsyncOperation asyncOp=AsyncOperationManager.CreateOperation(空);
动作完成=e=>
{
如果(已完成!=null)asyncOp.Post(委托
{
已完成(来源、目的地、e);
},空);
};
异步回调rc=null;
rc=readResult=>
{
尝试
{
int read=source.EndRead(readResult);
如果(读取>0)
{
destination.BeginWrite(缓冲区,0,读,写结果=>
{
尝试
{
destination.EndWrite(writeResult);
来源:beginhead(
buffer,0,buffer.Length,rc,null);
}
捕获(异常exc){done(exc);}
},空);
}
否则完成(空);
}
捕获(异常exc){done(exc);}
};
BeginRead(buffer,0,buffer.Length,rc,null);

AFAIK,没有高级异步API来复制文件。但是,您可以使用
Stream.BeginRead/EndRead
Stream.BeginWrite/EndWrite
API构建自己的API来完成该任务。或者,您可以使用
BeginInvoke/EndInvoke
方法,如这里的答案所述,但您必须保持在mi中nd,它们不会是非阻塞异步I/O。它们只在单独的线程上执行任务。

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

异步的思想编程就是允许调用线程(假设它是线程池线程)在异步IO完成时返回线程池以用于其他任务。在后台,调用上下文将填充到数据结构中,1个或多个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);
    }
  }
}
不过我还没有对此做过广泛的性能测试。我有点担心,因为如果它这么简单的话,它已经在核心库中了

await做了我在幕后描述的事情。如果你想大致了解它的工作原理,了解Jeff Richter的AsyncEnumerator可能会有所帮助。它们可能不完全相同,但想法非常接近。如果你从“async”方法中查看调用堆栈,你会看到MoveNext


就移动而言,如果它真的是“移动”,则不需要异步而不是复制然后删除。移动是针对文件表的快速原子操作。只有当您不尝试将文件移动到其他分区时,它才起作用。

这里有一个异步文件复制方法,它向操作系统提示我们按顺序读写,以便它可以在读取时预取数据并准备就绪至于写作:

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

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


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

我认为问题是异步执行操作,而不消耗线程。有多种方法可以将工作委托给线程池,其中大多数比此处的机制更简单。请你告诉我,它是什么意思(wait source.CopyToAsync(destination);)?在标记为aync的方法中,wait在内部所做的是等待等待等待的代码片段完成。天真地说,我们可以说它阻塞了。但是它并没有真正阻塞。真正的阻塞行为就像wait()使活动线程在执行点停留。Await实际上会使线程正在执行的任何操作的上下文停留在数据结构中,并允许活动线程返回到线程池,在该线程池中它可以用于其他事情。当Await返回线程池线程时(可能不是同一个线程)检索内容
  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);
  }