Copie / déplacement de fichier asynchrone dans C #

Quelle est la manière correcte de copier / déplacer des fichiers de manière asynchrone en C #?

L’idée de la programmation asynchrone est de permettre au thread appelant (en supposant qu’il s’agit d’un thread de pool de threads) de retourner dans le pool de threads pour être utilisé sur une autre tâche à la fin de l’async IO. Sous le capot, le contexte d’appel est intégré à une structure de données et 1 ou plusieurs threads d’achèvement d’E / S surveillent l’appel en attente. Lorsque IO termine le thread d’achèvement appelle sur un pool de threads en restauration du contexte d’appel. De cette façon, au lieu de bloquer 100 threads, il n’y a que les threads d’achèvement et quelques threads de pool de threads qui sont généralement inactifs.

Le mieux que je puisse trouver est:

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

Je n’ai pas fait de tests approfondis à ce sujet. Je suis un peu inquiet parce que si c’était aussi simple, ce serait déjà dans les bibliothèques principales.

attend ce que je décris dans les coulisses. Si vous souhaitez avoir une idée générale de son fonctionnement, il serait probablement utile de comprendre AsyncEnumerator de Jeff Richter. Ils pourraient ne pas être complètement la même ligne pour la ligne, mais les idées sont vraiment proches. Si vous regardez une stack d’appels d’une méthode “async”, vous verrez MoveNext dessus.

En ce qui concerne le mouvement, il n’a pas besoin d’être asynchrone si c’est vraiment un “Move” et non une copie, puis effacez. Move est une opération atomique rapide sur la table de fichiers. Cela ne fonctionne que si vous n’essayez pas de déplacer le fichier sur une autre partition.

Voici une méthode de copie de fichier asynchrone qui donne les indications du système d’exploitation que nous lisons et écrivons de manière séquentielle, afin de pouvoir pré-extraire les données en lecture et de les préparer pour l’écriture:

 public static async Task CopyFileAsync(ssortingng sourceFile, ssortingng 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); } 

Vous pouvez également tester la taille du tampon. Voici 4096 octets.

J’ai amélioré le code par @DrewNoakes légèrement (performance et annulation):

  public static async Task CopyFileAsync(ssortingng sourceFile, ssortingng 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); } 

Vous pouvez utiliser des delegates asynchrones

 public class AsyncFileCopier { public delegate void FileCopyDelegate(ssortingng sourceFile, ssortingng destFile); public static void AsynFileCopy(ssortingng sourceFile, ssortingng destFile) { FileCopyDelegate del = new FileCopyDelegate(FileCopy); IAsyncResult result = del.BeginInvoke(sourceFile, destFile, CallBackAfterFileCopied, null); } public static void FileCopy(ssortingng sourceFile, ssortingng destFile) { // Code to copy the file } public static void CallBackAfterFileCopied(IAsyncResult result) { // Code to be run after file copy is done } } 

Vous pouvez l’appeler comme:

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

Ce lien vous indique les différentes techniques de codage asynchrone

Vous pouvez le faire comme suggéré dans cet article:

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

Bien qu’il y ait certaines circonstances où vous voudriez éviter Task.Run , Task.Run(() => File.Move(source, dest) fonctionnera. Cela vaut la peine d’envisager car lorsqu’un fichier est simplement déplacé sur le même disque / volume, il s’agit d’une opération quasi instantanée, car les en-têtes sont modifiés, mais le contenu du fichier n’est pas déplacé. Les différentes méthodes asynchrones “pures” copient invariablement le stream, même s’il n’est pas nécessaire de le faire, et par conséquent peut être un peu plus lent dans la pratique.

AFAIK, il n’y a pas d’API asynchrone de haut niveau pour copier un fichier. Cependant, vous pouvez créer votre propre API pour accomplir cette tâche à l’aide des Stream.BeginRead/EndRead et Stream.BeginWrite/EndWrite . Alternativement, vous pouvez utiliser la méthode BeginInvoke/EndInvoke comme indiqué dans les réponses ici, mais vous devez garder à l’esprit qu’elles ne seront pas des E / S asynchrones non bloquantes. Ils exécutent simplement la tâche sur un thread séparé.

La manière correcte de copier: utilisez un thread séparé.

Voici comment vous pouvez le faire (de manière synchrone):

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

Voici comment procéder de manière asynchrone:

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

C’est une façon très naïve de faire les choses. Bien faite, la solution inclura une méthode d’événement / délégué pour signaler le statut de la copie du fichier et notifier les événements importants comme l’échec, l’achèvement, etc.

acclamations, jrh

Je suggère que la fonction d’E / S de copie de fichier, disponible dans les langages de programmation .Net, est de toute façon asynchrone. Après l’avoir utilisé dans mon programme pour déplacer de petits fichiers, il apparaît que les instructions suivantes commencent à s’exécuter avant la fin de la copie du fichier. Je vous dis que l’exécutable donne à Windows la tâche de faire la copie, puis retourne immédiatement pour exécuter l’instruction suivante – sans attendre la fin de Windows. Cela m’oblige à construire des boucles while juste après l’appel à copier qui s’exécuteront jusqu’à ce que je puisse confirmer que la copie est terminée.