Cópia de arquivo assíncrona / Mover em C #

Qual é a maneira correta de copiar arquivos / mover de forma assíncrona em c #?

A idéia de programação assíncrona é permitir que o encadeamento de chamada (assumindo que seja um encadeamento de encadeamento de encadeamentos) retorne ao conjunto de encadeamentos para uso em alguma outra tarefa enquanto o I / O asynchronous é concluído. Sob o capô, o contexto da chamada é preenchido em uma estrutura de dados e 1 ou mais threads de conclusão de E / S monitoram a chamada em espera pela conclusão. Quando o pedido de inserção conclui o encadeamento de conclusão, ele é chamado de volta para um pool de encadeamentos que restaura o contexto da chamada. Dessa forma, em vez de 100 encadeamentos de threads, há apenas os encadeamentos de conclusão e alguns encadeamentos de encadeamentos de encadeamentos que ficam praticamente inativos.

O melhor que posso encontrar é:

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

Eu não fiz testes exaustivos sobre isso. Estou um pouco preocupado porque, se fosse assim tão simples, já estaria nas bibliotecas principais.

aguardo faz o que estou descrevendo nos bastidores. Se você quiser ter uma ideia geral de como funciona, provavelmente ajudará a entender o AsyncEnumerator de Jeff Richter. Eles podem não ser completamente a mesma linha para linha, mas as ideias são realmente próximas. Se você olhar para uma pilha de chamadas de um método “async”, verá MoveNext nela.

No que diz respeito a movimentos, não precisa de ser asynchronous se for realmente um “Move” e não uma cópia e, em seguida, eliminar. Mover é uma operação atômica rápida contra a tabela de arquivos. Ele só funciona dessa forma, se você não tentar mover o arquivo para uma partição diferente.

Aqui está um método de cópia de arquivo asynchronous que fornece as dicas do sistema operacional que estamos lendo e escrevendo em seqüência, para que ele possa pré-buscar dados na leitura e ter as coisas prontas para a gravação:

 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); } 

Você também pode experimentar o tamanho do buffer. Aqui está 4096 bytes.

Eu melhorei o código por @DrewNoakes um pouco (desempenho e cancelamento):

  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); } 

Você pode usar delegates asynchronouss

 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 } } 

Você pode chamá-lo como:

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

Este link informa as diferentes técnicas de codificação asyn

Você pode fazer isso como este artigo sugeriu:

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

Embora existam algumas circunstâncias em que você gostaria de evitar Task.Run , Task.Run(() => File.Move(source, dest) funcionará. Vale a pena considerar, porque quando um arquivo é simplesmente movido no mesmo disco / volume, é uma operação quase instantânea, pois os headers são alterados, mas o conteúdo do arquivo não é movido Os vários methods asynchronouss “puros” invariavelmente copiam o stream, mesmo quando não há necessidade de fazer isso, e como resultado pode ser um pouco mais lento na prática.

AFAIK, não há APIs assíncronas de alto nível para copiar um arquivo. No entanto, você pode criar sua própria API para realizar essa tarefa usando as APIs Stream.BeginRead/EndRead e Stream.BeginWrite/EndWrite . Alternativamente, você pode usar o método BeginInvoke/EndInvoke como mencionado nas respostas aqui, mas você deve ter em mente que eles não serão I / O asynchronous sem bloqueio. Eles simplesmente executam a tarefa em um thread separado.

A maneira correta de copiar: use um thread separado.

Veja como você pode estar fazendo isso (de forma síncrona):

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

Veja como fazer isso de forma assíncrona:

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

Esta é uma maneira muito ingênua de fazer as coisas. Bem feito, a solução includeia algum método de evento / delegado para relatar o status da cópia do arquivo e notificar events importantes como falha, conclusão etc.

elogios, jrh

Eu sugeriria que a function File Copy IO, disponível nas linguagens de programação .Net, seja assíncrona em qualquer caso. Depois de usá-lo no meu programa para mover arquivos pequenos, parece que as instruções subsequentes começam a ser executadas antes que a cópia do arquivo real seja concluída. Eu estou ginging que o executável dá ao Windows a tarefa de fazer a cópia e, em seguida, imediatamente retorna para executar a próxima instrução – não esperando para o Windows terminar. Isso me obriga a construir loops while logo após a chamada para a cópia que será executada até que eu possa confirmar que a cópia está completa.