@big4oot

Как асинхронно (параллельно) скопировать файлы?

Всем привет. Пытаюсь разобраться с асинхронными методами на C#. Есть следующая простая задача:
Скопировать 2 файла из одного Windows-пк на другой (оба в одной локальной сети). В реальности файлов может быть ~1000, но для упрощения уменьшил до двух.
Код

using System.Diagnostics;
public static class Program
{
    public static async Task Main()
    {
        var destinationPath = @"path\to\destination\folder";
        List<string> filePaths = new()
        {
            @"\\remote-pc\c$\files\file1",
            @"\\remote-pc\c$\files\file2",
        };
        
        var watch = Stopwatch.StartNew();
        List<Task> tasks = new List<Task>();
        foreach (string path in filePaths)
        {
            Console.WriteLine($"Start copy {Path.GetFileName(path)}");
            using var sourceStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true);
            using var destinationStream = new FileStream(Path.Combine(destinationPath, Path.GetFileName(path)), FileMode.CreateNew, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true);
            var task = sourceStream.CopyToAsync(destinationStream).ContinueWith(_ => {
                Console.WriteLine($"End copy {Path.GetFileName(path)}");
            });
            tasks.Add(task);
        }
        
        await Task.WhenAll(tasks);
        watch.Stop();
        var elapsedMs = watch.ElapsedMilliseconds;
        Console.WriteLine(elapsedMs);
    }
}


Если я правильно понимаю, async/await необходимо использовать в I/O-bound операциях - как раз этот случай (или ошибаюсь?). При выполнении строки sourceStream.CopyToAsync(destinationStream) вызывающий поток будет освобожден для выполнения следующей операции (видимо, это правда, так при запуске одновременно
появяются два WriteLine'a о начале копирования файлов).
Однако, анализируя время выполнения кода (elapsedMs), которое равно ~30 с. для двух файлов, делалю вывод, что файлы копируются вовсе не параллельно. При запуске копирования каждого файла отдельно, время выполнения ~20 с. и ~6 c. для каждого файла соответственно. Cледовательно при "паралелльном копировании" ожидаю общее время выполнения = времени копирования самого большого файла.

Прошу помочь разобраться в моих рассуждениях.
  • Вопрос задан
  • 127 просмотров
Пригласить эксперта
Ответы на вопрос 3
@Ilay_Developer
Ответ написан
Комментировать
vabka
@vabka Куратор тега C#
Токсичный шарпист
1. у тебя обращение к стримам завёрнуто в using - они могут быть уничтожены до того как копирование завершится.
Убери using var и сохраняй стримы вместе с таской. Диспозь уже после завершения таски
2. Кажется, тебе может быть интересен Parallel Linq.
Ответ написан
Комментировать
Использовать ли асинхронность и отправлять ли файлы параллельно - это два разных вопроса. Асинхронность имеет смысл, если вы не хотите блочить основной поток в клиентских приложениях, или хотите экономить потоки в серверных приложениях.
Параллельная отправка файлов скорее всего смысла не имеет, вы будете всё равно упираться либо в сеть, либо в диск. Больше и смысла в том, чтобы увеличить размер буфера, или сжать файлы в архив перед отправкой.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы