Эффективный алгоритм для асинхронного копирования папки

Часть моего проекта отвечает за копирование каталогов из одного места в другое. Сначала рекурсивно получаю список файлов в папке (отбрасывая файлы с исключениями), затем с этим списком работаю. Проблема состоит в производительности. Копировать файлы по одному неэффективно. Однако, если для каждого выделить поток (или Task), это тоже плохое решение, потому что:

  1. Количество и размер файлов могут быть разными.
  2. Если чтение/запись происходит в пределах одного ЖЕСТКОГО диска, то при слишком частом обращении в разные каталоги он будет физически не успевать и производительность уменьшится...
  3. Если при копировании одного из файлов возникает исключение, то программа жестко зависает на несколько секунд (проверял)

Нужен особый алгоритм, который будет совмещать синхронность и асинхронность. Например, находить какую-то зависимость между количеством и размером файлов, делить их на группы и др.

Наставьте меня на правильное решение, пожалуйста.


Ответы (2 шт):

Автор решения: Sergey Kovalev

Вариант 1. Тупой.

  1. Создать общую очередь файлов для копирования, используя ConcurrentQueue и общий CancellationToken.
  2. Создать N потоков для копирования файлов через Task.Run() с тем самым CancellationToken. Потоки забирают задание из очереди и копируют файл за файлом. Как только очедерь пуста и IsCancellationRequested=true, завершаем поток.
  3. Создать поток, который обходит папку и наполняет очередь. Как только все файлы найдены, выставляем IsCancellationRequested=true.

Вариант 2. Лучше.

Получаем список файлов в IEnumerable и используем Parallell.ForEach(). Ну или через LINQ: someList.AsParallel().ForEach((filePath)=> {...copy...})).

.NET TPL попытается подобрать оптимальное количество потоков автоматически.

Вариант 3. Reactive. Модно и современно.

Использовать observer pattern через The Reactive Extensions for .NET https://github.com/dotnet/reactive

Делить на группы вручную по размеру не имеет смысла, а вот подбирать количество потоков, чтобы не убить IO и не поставить жёсткие диски на колени -- имеет.

→ Ссылка
Автор решения: Qwertiy

Зависит от способа копирования, но в большинстве случаев наоборот эффективнее копировать по одному. А вот шаг составления списка файлов мне кажется лишним. Нашли файл, копируем.

→ Ссылка