2009-05-19 33 views

Respuesta

-1

La forma correcta de copiar: use una secuencia separada.

Así es como que podría estar haciendo que (sincrónicamente):

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

Aquí es cómo hacerlo de forma asíncrona:

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

Esta es una manera muy ingenua para hacer las cosas. Si se hace bien, la solución sería incluir algún método de evento/delegado para informar del estado de la copia de archivos, y notificar eventos importantes como el fracaso, etc. finalización

aplausos, JRH

6

Puede utilizar delegados asíncronos

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

se le puede llamar como:

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

Este link le indica los diferentes técni ques de asyn de codificación

+4

Creo que la pregunta era hacer la operación de forma asíncrona, sin consumir un hilo. Existen múltiples formas de delegar trabajo en el grupo de subprocesos, la mayoría de los cuales son más fáciles que el mecanismo aquí. –

5

Usted puede hacerlo como this artículo sugirieron:

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

Las transmisiones ahora tienen una operación de copia incorporada que hace esto mucho más fácil. Pero mi problema con esta técnica es que siempre copia el archivo, incluso cuando está en el mismo disco y no es necesaria tal operación. – Casey

2

yo sepa, no hay asincrónica API de alto nivel para copiar un archivo. Sin embargo, puede construir su propia API para realizar esa tarea utilizando las API Stream.BeginRead/EndRead y Stream.BeginWrite/EndWrite. Alternativamente, puede usar el método BeginInvoke/EndInvoke como se menciona en las respuestas aquí, pero debe tener en cuenta que no bloquearán la E/S asincrónica. Simplemente realizan la tarea en un hilo separado.

-2

Sugeriría que la función File Copy IO, disponible en los lenguajes de programación .Net, sea asíncrona en cualquier caso. Después de usarlo dentro de mi programa para mover archivos pequeños, parece que las instrucciones subsiguientes comienzan a ejecutarse antes de que se termine la copia del archivo. Estoy dando vueltas que el ejecutable le da a Windows la tarea de hacer la copia y luego regresa inmediatamente para ejecutar la siguiente instrucción, sin esperar a que Windows termine. Esto me obliga a construir while loops justo después de la llamada para copiar que se ejecutará hasta que pueda confirmar que la copia está completa.

+4

La razón por la que funciona es que si mueve un archivo dentro de la misma unidad, no es necesario volver a escribir nada, excepto los encabezados. Si te mudas a un disco diferente, puedes convencerte de que no es una operación asíncrona. – Casey

+0

Y para extender la respuesta de Casey, copiar archivos a través de VPN o WAN en general es mucho más lento. –

32

La idea de la programación asincrónica es permitir que el subproceso de llamada (suponiendo que sea un subproceso de agrupación de subprocesos) vuelva al grupo de subprocesos para utilizarlo en alguna otra tarea mientras finaliza la asincronización de E/S. Bajo el capó, el contexto de la llamada se rellena en una estructura de datos y 1 o más subprocesos de finalización de E/S supervisan la llamada en espera de completarse. Cuando IO completa el hilo de finalización vuelve a invocar un grupo de subprocesos que restaura el contexto de llamada. De esta forma, en lugar de bloquear 100 hilos, solo quedan los subprocesos de finalización y algunos subprocesos de grupo de subprocesos prácticamente inactivos.

Lo mejor que puedo llegar a decir:

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

no he hecho numerosas pruebas Potencia de esto, sin embargo. Estoy un poco preocupado porque si fuera así de simple, ya estaría en las bibliotecas principales.

aguarde hace lo que estoy describiendo detrás de escena.Si desea obtener una idea general de cómo funciona, probablemente le ayude entender el AsyncEnumerator de Jeff Richter. Puede que no sean completamente la misma línea por línea, pero las ideas son muy cercanas. Si alguna vez observas una pila de llamadas desde un método "asíncrono", verás MoveNext en ella.

En lo que respecta al movimiento, no tiene que ser asincrónico si se trata realmente de un "Movimiento" y no una copia, y luego eliminar. Move es una operación atómica rápida contra la tabla de archivos. Sin embargo, solo funciona de esa manera si no intenta mover el archivo a una partición diferente.

+0

¿Puede decirme qué significa (aguarde source.CopyToAsync (destination);)? –

+2

Internamente, lo que espera en un método marcado como aync es esperar a que se complete el código de código esperado. Ingenuamente podemos decir que bloquea. Sin embargo, realmente no bloquea. Un comportamiento de bloqueo real como Wait() mantiene el hilo activo atorado en el punto de ejecución. En realidad, esperar hace que el contexto de lo que sea que esté haciendo el hilo quede atascado en una estructura de datos y permite que el hilo activo regrese al grupo de hilos donde puede usarse para otra cosa. Cuando espera devuelve un hilo del grupo de subprocesos (probablemente no el mismo) recupera el contexto y reanuda la ejecución. – csaam

+0

Eso no parece gran cosa, pero usar asincrón correctamente puede significar reducir el número real de hilos activos en ejecución. En io servicios intensivos esto puede ser un gran problema. He escrito un código que puede tener 80 solicitudes concurrentes activas, pero solo 5 o más hilos activos. El resultado neto es un mayor uso de la CPU pero también un mayor rendimiento para mi servicio por instancia. Así que estás obteniendo más por tu dinero de tu hardware. – csaam

16

He aquí un método de copia de archivos asincrónica que da los consejos OS que estamos leyendo y escribiendo de forma secuencial, de modo que pueda precargar datos sobre la lectura y tener las cosas listas para la escritura:

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

También puedes experimentar con el tamaño del búfer. Aquí están sus 4096 bytes.

+0

¿Qué sucede exactamente después de la primera línea de código? ¿Libera el hilo hasta que se realiza la precarga de los datos del archivo? – BornToCode

+0

Runtime no impone ninguna garantía. Esto es lo que todos esperamos que suceda: si la solicitud se puede atender sin esperar recursos externos, entonces aguardar se completará sincrónicamente. De lo contrario, se capturará el estado, el contexto de subprocesamiento y todo, se producirá el subproceso y la continuación se ejecutará una vez que se complete la solicitud. En mi código mejorado a continuación, el contexto de enhebrado no se captura. Esto significa que posiblemente se ejecutará un hilo diferente del grupo de finalización de E/S. – GregC

8

que hemos mejorado código de @DrewNoakes ligeramente (rendimiento y cancelación):

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

Esto puede ser engañoso, si estamos trabajando en la aplicación de interfaz gráfica de usuario queremos volver al contexto capturado. Esta debe ser una decisión del usuario, un nivel más alto ('aguarde CopyFileAsync(). ConfigureAwait (falso)' – Nekromancer

+0

Estoy de acuerdo. El equipo de la Biblioteca de clases base recomienda posponer la configuración de comportamiento de captura de contexto a la persona que llama. El código no captura el contexto. – GregC

3

Si bien hay algunas circunstancias en las que te gustaría evitar Task.Run, Task.Run(() => File.Move(source, dest) va a funcionar. Vale la pena considerarlo porque cuando un archivo simplemente se mueve en el mismo disco/volumen, es una operación casi instantánea, ya que los encabezados se cambian pero el contenido del archivo no se mueve. Los diversos métodos asincrónicos "puros" invariablemente copian el flujo, incluso cuando no hay necesidad de hacerlo, y como resultado puede ser bastante más lento en la práctica.

+0

es que al mover archivos en el mismo volumen y simplemente cambia los encabezados, esto utiliza un hilo innecesario. – IllidanS4