2015-12-30 6 views
3

Я использую следующий метод для копирования содержимого каталога в другой каталог.Метод копирования больших объемов данных в C#

public void DirCopy(string SourcePath, string DestinationPath) 
    { 
     if (Directory.Exists(DestinationPath)) 
     { 
      System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(DestinationPath); 

      foreach (FileInfo file in downloadedMessageInfo.GetFiles()) 
      { 
       file.Delete(); 
      } 
      foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories()) 
      { 
       dir.Delete(true); 
      } 
     } 



     //================================================================================= 
     string[] directories = System.IO.Directory.GetDirectories(SourcePath, "*.*", SearchOption.AllDirectories); 

     Parallel.ForEach(directories, dirPath => 
     { 
      Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath)); 
     }); 

     string[] files = System.IO.Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories); 

     Parallel.ForEach(files, newPath => 
     { 
      File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true); 
     }); 

    } 

Моя единственная проблема в том, что есть совсем немного данных в исходном пути и программа становится не реагирует, а копирование происходит.

Мне интересно, какие у меня варианты для копирования данных. Я провел некоторое исследование, и кто-то рекомендовал использовать буфер.

Я действительно не видел решения, которые я понимаю особенно хорошо, поэтому любая помощь/ресурсы, которые являются ясными и лаконичными, были бы замечательными.

Спасибо за помощь!

+0

Вы можете использовать отдельный поток, чтобы запустить его вместо того, чтобы запустить его в потоке пользовательского интерфейса. –

+0

Можете ли вы количественно определить некоторые вещи, которые вы копируете? Как каталоги, файлы, количество данных, которые вы передаете, вы можете использовать неправильный инструмент для задания – konkked

+0

Это приложение для пользовательского интерфейса? Если это так, выполните эту работу в фоновом потоке. Например, с помощью 'Task.Run' –

ответ

2

Выполнение длинных задач в Windows Forms, на потоке сообщений вызовет форму, чтобы стать не отвечает, пока задача не будет выполнена. Для предотвращения этого вам понадобится использовать потоки.Он может получить сложно, но вы будете нуждаться в BackgroundWorker:

_Worker = new BackgroundWorker(); 
_Worker.WorkerReportsProgress = true; 
_Worker.DoWork += Worker_DoWork; 
_Worker.ProgressChanged += Worker_ProgressChanged; 
_Worker.RunWorkerAsync(); 

Метод, который преформ задачу:

private void Worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    BackgroundWorker worker = sender as BackgroundWorker; 
    worker.ReportProgress(1); 

    // do stuff 

    worker.ReportProgress(100); 
} 

И способ сообщить о прогрессе:

private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    switch (e.ProgressPercentage) 
    { 
     case 1: 
      // make your status bar visible 
      break; 

     case 100: 
      // hide it again 
      break; 
    } 
} 

Вы можете использовать индикатор выполнения marquee, но если вы хотите сообщить фактический процент назад, вычисление размеров файлов и прогресс в вашем методе Worker_DoWork может осложниться и это еще одна проблема.

https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx

3

Если ваша цель - остановить приложение от перехода в нечувствительное состояние, предлагаемый метод использования буферов не устранит вашу проблему. Вместо этого посмотрите на использование отдельного Thread, чтобы скопировать ваши каталоги.

Еще лучше, используйте BackgroundWorker, что имеет дополнительное преимущество в том, что вы можете сообщить о прогрессе.

+0

Да, используйте фона рабочего. Ваше приложение будет оставаться отзывчивым, вы можете обновлять переменные в основном процессе для достижения прогресса и выходить, когда он будет завершен. –

2

Быстрое исправление для вашего вопроса является использование фонового потока в вызывающем коде, как это:

var source_directory = "c:\\source"; 
var destination_directory= "c:\\destination"; 
Task.Run(() => DirCopy(source_directory, destination_directory)); 

В этом примере используется Task.Run method, который использует один из потоков пула потоков для выполнения кода.

Это гарантирует, что поток пользовательского интерфейса может обновлять пользовательский интерфейс и отвечать на ввод пользователя.

1

Если предположить, что реальная проблема заключается ваша программа становится не реагирующими ...

Вашей программы, вероятно, перестает отвечать на запросы, потому что нить вы используете для выполнения копий является потоком вы левередж реагировать на ввод данных пользователя. Если вы хотите, чтобы копии продолжались в фоновом режиме, пока программа оставалась отзывчивой, вы должны выполнить копии асинхронно. (Я предполагаю, что вы используете winforms или wpf на основе вашего контекста.)

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

(Ваш Parallel.ForEach не делает трюк, потому что поток, который вызывает это не будет продолжаться до Parallel.ForEach завершения выполнения)

2

Это не ясно, какая версия компилятора/рамки вы используете, но вы можете использовать асинхронные операции с файлами и не придется беспокоиться о многопоточности. Вы также можете воспользоваться потоковыми версиями EnumerateDirectories и EnumerateFiles, если у вас есть большие иерархии файлов.

public async Task DirCopy(string SourcePath, string DestinationPath) 
{ 
    //slightly different from your code, in that the destination directory is simply removed recursively 
    Directory.Delete(DestinationPath, true); 

    //enumerate files returns earlier than get files for large file hierarchies 
    //... because it is a streaming IEnumerable instead of an array 
    foreach (var sourcePath in System.IO.Directory.EnumerateFiles(SourcePath, "*.*", SearchOption.AllDirectories)) 
    { 
     var destinationPath = sourcePath.Replace(SourcePath, DestinationPath); 

     //slightly different from your code, in that directories are created as needed 
     //... however, this would mean empty directories are not copied 
     Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); 

     using (var source = File.Open(sourcePath, FileMode.Open, FileAccess.Read)) 
     using (var destination = File.Create(destinationPath)) 
     { 
      //async copy of the file frees the current thread 
      //... e.g. for the UI thread to process UI updates 
      await source.CopyToAsync(destination); 
     } 
    } 
} 
0

Спасибо всем, что я очень ценю все материалы, чтобы увидеть различные способы достижения этого. На данный момент я решил просто выполнить Task.Run, но я собираюсь изучить работу рабочего и асинхронного фона.

Еще раз спасибо всем!

Для справки я только что сделал

Task.Run(()=>{ DirCopy("source","destination"); }); 

DirCopy

public void DirCopy(string SourcePath, string DestinationPath) 
    { 
     if (Directory.Exists(DestinationPath)) 
     { 
      System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(DestinationPath); 

      foreach (FileInfo file in downloadedMessageInfo.GetFiles()) 
      { 
       file.Delete(); 
      } 
      foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories()) 
      { 
       dir.Delete(true); 
      } 
     } 



     //================================================================================= 
     string[] directories = System.IO.Directory.GetDirectories(SourcePath, "*.*", SearchOption.AllDirectories); 
     string[] files = System.IO.Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories); 

     totalPB.Minimum = 0; 
     totalPB.Maximum = directories.Length; 
     totalPB.Value = 0; 
     totalPB.Step = 1; 

     subTotalPB.Minimum = 0; 
     subTotalPB.Maximum = directories.Length; 
     subTotalPB.Value = 0; 
     subTotalPB.Step = 1; 

     Parallel.ForEach(directories, dirPath => 
     { 
      Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath)); 
      subTotalPB.PerformStep(); 
     }); 

     Task.Run(() => 
     { 

      Parallel.ForEach(files, newPath => 
      { 
       File.Copy(newPath, newPath.Replace(SourcePath, DestinationPath), true); 
       totalPB.PerformStep(); 
      }); 

     }); 



    } 

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

  • Нет связанных вопросов^_^