2009-05-19 3 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 говорит вам различные техни Ques из ASYN кодирования

+4

Я думаю, что вопрос состоял в том, чтобы выполнить операцию асинхронно, не потребляя нить. Существует несколько способов делегирования работы в threadpool, большинство из которых проще, чем механизм здесь. –

5

Вы можете сделать это, как this статье предлагается:

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

2

AFAIK, нет высокого уровня асинхронной API, чтобы скопировать файл. Однако вы можете создать свой собственный API для выполнения этой задачи с использованием API-интерфейсов Stream.BeginRead/EndRead и Stream.BeginWrite/EndWrite. В качестве альтернативы вы можете использовать метод BeginInvoke/EndInvoke, как указано в ответах здесь, но вы должны иметь в виду, что они не будут блокировать асинхронный ввод-вывод. Они просто выполняют задачу в отдельном потоке.

-2

Я бы предположил, что функция File Copy IO, доступная на языках программирования .Net, в любом случае является асинхронной. После использования его в моей программе для перемещения небольших файлов, кажется, что последующие инструкции начинают выполняться до завершения фактической копии файла. Я разбираюсь в том, что исполняемый файл дает Windows задачу сделать копию, а затем сразу же возвращается для выполнения следующей инструкции - не дожидаясь завершения Windows. Это заставляет меня конструировать в то время как циклы сразу после вызова для копирования, который будет выполняться, пока я не смогу подтвердить, что копия завершена.

+4

Причина, по которой работает, заключается в том, что если вы перемещаете файл на одном диске, ничего не нужно переписывать, кроме заголовков. Если вы перейдете на другой диск, вы можете убедиться, что это не асинхронная операция. – Casey

+0

И, чтобы продлить ответ Кейси, копирование файлов через VPN или WAN в целом является медленным. –

32

Идея асинхронного программирования состоит в том, чтобы позволить вызывающему потоку (при условии, что это поток потока потоков) вернуться в пул потоков для использования в какой-либо другой задаче, в то время как асинхронный ввод-вывод завершен. Под капотом контекст вызова заполняется в структуру данных, и один или несколько потоков завершения ввода-вывода контролируют ожидающий завершения вызов. Когда 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); 
    } 
    } 
} 

Я не сделал обширное тестирование перфорация на этом, хотя. Я немного волнуюсь, потому что, если бы это было так просто, это уже было бы в основных библиотеках.

ждет, что я описываю за кулисами.Если вы хотите получить общее представление о том, как это работает, это, вероятно, поможет понять AsyncEnumerator Джеффа Рихтера. Они могут быть не совсем одинаковыми линиями, но идеи действительно близки. Если вы когда-либо смотрите на стек вызовов из метода «async», вы увидите MoveNext на нем.

Что касается перемещения, ему не нужно асинхронно, если это действительно «Перемещение», а не копия, а затем удаление. Перемещение - это быстрая атомная операция против таблицы файлов. Это работает только так, но если вы не пытаетесь переместить файл в другой раздел.

+0

Пожалуйста, скажите мне, что это значит (ждите source.CopyToAsync (destination);)? –

+2

Внутренне то, что ожидает в методе, помеченном как aync, - это ожидание ожидаемого набора кода. Наивно мы можем сказать, что это блокирует. Однако он не блокируется. Реальное блокирующее поведение, такое как Wait(), удерживает активный поток, застрявший в точке выполнения. Ожидание фактически вызывает контекст того, что поток делает, чтобы застревать в структуре данных и позволяет активному потоку возвращаться в пул потоков, где его можно использовать для чего-то другого. Когда ожидание возвращает поток потока потока (возможно, не тот же самый), извлекает контекст и возобновляет выполнение. – csaam

+0

Это не похоже на то, что большая сделка, но с использованием асинхронизации правильно может означать сокращение количества фактических активных потоков. В интенсивных услугах это может быть большой проблемой. Я написал код, который может содержать 80 активных одновременных запросов, но только 5 или около того активных потоков. Конечным результатом является более высокое использование процессора, но также более высокая пропускная способность для моего сервиса на один экземпляр. Таким образом, вы получаете больше ударов за свои деньги из своего оборудования. – csaam

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 байт.

+0

Что именно происходит после первой строки кода? освобождает ли этот поток до тех пор, пока не будет выполнена предварительная выборка данных из файла? – BornToCode

+0

Runtime не предоставляет никаких гарантий. Вот что мы все надеемся: если запрос можно обслуживать, не дожидаясь внешних ресурсов, то ожидание завершится синхронно. В противном случае будет зафиксировано состояние, контекст потока и все, поток будет выдаваться, и продолжение будет выполняться после завершения запроса. В моем расширенном коде ниже контекст потоковой передачи не фиксируется. Это означает, что, возможно, будет запущен другой поток из пула завершения ввода-вывода. – GregC

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

Это может ввести в заблуждение, если мы работаем над GUI-приложением, мы хотим вернуться в захваченный контекст. Это должно быть решение пользователя, более высокий уровень ('await CopyFileAsync(). ConfigureAwait (false)' – Nekromancer

+0

Я согласен. Команда библиотеки базового класса рекомендует отложить настройку конфигурации захвата контекста для вызывающего. Код не фиксирует контекст. – GregC

3

Хотя есть некоторые обстоятельства, при которых вы хотите избежать Task.Run, Task.Run(() => File.Move(source, dest) будет работать. Это стоит учитывать, потому что, когда файл просто перемещается на том же диске/томе, это почти мгновенная операция, так как заголовки изменены, но содержимое файла не перемещается. Различные «чистые» асинхронные методы неизменно копируют поток, даже если нет необходимости делать это, и в результате на практике это может быть немного медленнее.

+0

Проблема заключается в том, что при перемещении файлов на том же томе, и он просто меняет заголовки, это использует ненужный поток. – IllidanS4

Смежные вопросы