Эффективный алгоритм для асинхронного копирования папки
Часть моего проекта отвечает за копирование каталогов из одного места в другое. Сначала рекурсивно получаю список файлов в папке (отбрасывая файлы с исключениями), затем с этим списком работаю. Проблема состоит в производительности. Копировать файлы по одному неэффективно. Однако, если для каждого выделить поток (или Task), это тоже плохое решение, потому что:
- Количество и размер файлов могут быть разными.
- Если чтение/запись происходит в пределах одного ЖЕСТКОГО диска, то при слишком частом обращении в разные каталоги он будет физически не успевать и производительность уменьшится...
- Если при копировании одного из файлов возникает исключение, то программа жестко зависает на несколько секунд (проверял)
Нужен особый алгоритм, который будет совмещать синхронность и асинхронность. Например, находить какую-то зависимость между количеством и размером файлов, делить их на группы и др.
Наставьте меня на правильное решение, пожалуйста.
Ответы (2 шт):
Вариант 1. Тупой.
- Создать общую очередь файлов для копирования, используя
ConcurrentQueueи общийCancellationToken. - Создать N потоков для копирования файлов через
Task.Run()с тем самымCancellationToken. Потоки забирают задание из очереди и копируют файл за файлом. Как только очедерь пуста иIsCancellationRequested=true, завершаем поток. - Создать поток, который обходит папку и наполняет очередь. Как только все файлы найдены, выставляем
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 и не поставить жёсткие диски на колени -- имеет.
Зависит от способа копирования, но в большинстве случаев наоборот эффективнее копировать по одному. А вот шаг составления списка файлов мне кажется лишним. Нашли файл, копируем.