Вызовите асинхронный метод в новой задаче c # - PullRequest
0 голосов
/ 10 октября 2019

Проблема, которую я пытаюсь решить:

Для каждого каталога существует несколько файлов, и я хочу загрузить это в Azure.

Итак, я хочу сделать это: Задача1 -выгрузка файлов из каталога 1 в лазурь Задача 2 - выгрузка файлов из каталога 2 в лазурь

Я хочу сделать это одновременно.

У меня есть следующий код:

private async Task ProcessMatFiles(string directory, List<FileInfo> matFiles)
{
    foreach (var file in matFiles)
    {
        if (!string.IsNullOrEmpty(file.Name) && !string.IsNullOrEmpty(directory) && !string.IsNullOrEmpty(file.FullName))
        {
            var cloudBlockBlob = this._cloudBlobContainer.GetBlockBlobReference("textures/" + directory + "/" + file.Name);

            if (!await cloudBlockBlob.ExistsAsync())
                await cloudBlockBlob.UploadFromFileAsync(file.FullName);
        }
    }
List<Task> tasks = new List<Task>();
foreach (var directory in matFileDirectories)
{
    // Get all the files in the directory
    var matFiles = new DirectoryInfo(directory).EnumerateFiles().ToList();

    // Get the directory name of the files
    var matDirectory = Path.GetFileName(Path.GetDirectoryName(matFiles.FirstOrDefault().FullName));

    if (matFiles.Count > 0 && !string.IsNullOrEmpty(matDirectory))
    {
        var task = new Task(() =>this.ProcessMatFiles(matDirectory, matFiles));
        tasks.Add(task);
        task.Start();
    }
}

Task.WaitAll(tasks.ToArray());

С этим кодом я получаю следующее предупреждение:

Поскольку этот вызов не ожидается,выполнение текущего метода продолжается до завершения вызова. Попробуйте применить оператор 'await' к результату вызова.

Что это значит? Как это влияет на мой код?

Я могу удалить предупреждение, выполнив следующее:

var task = new Task(async () => await this.ProcessMatFiles());

Это правильный путь к этому?

1 Ответ

1 голос
/ 10 октября 2019

Проблема real заключается в том, как обрабатывать несколько файлов параллельно. ProcessMatFiles возвращает Task уже, и я предполагаю, что он не запускает ничего тяжелого в потоке вызывающего. Эта задача может быть сохранена в списке tasks. Этот список можно ожидать без блокировки с помощью

await Task.WhenAll(tasks);

. Лучшим решением было бы преобразование всего цикла в запрос LINQ, который возвращает задачи и ожидает его.

var tasks = from var directory in matFileDirectories
            let dir=new DirectoryInfo(directory)
            let files=dir.GetFiles()
            select ProcessMatFiles(dir.Name, files));

await Task.WhenAll(tasks);

Проблема в том, что перечисление файлов в папке само по себе дорого, и GetFiles(), или использование EnumerateFiles().ToList(), должно ждать завершения перечисления. Было бы лучше, если бы ProcessMatFiles получил объект DirectoryInfo и перечислил файлы в отдельном потоке .

Еще одним улучшением будет обработка файлов один за другим:

var tasks = from var directory in matFileDirectories
            let dir=new DirectoryInfo(directory)
            from file in dir.EnumerateFiles()
            select ProcessMatFile(dir.Name, file));

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

Обновление

Поскольку это операция загрузки файла, каждый файл является отдельной асинхронной операцией. Большинство проверок можно удалить при работе с объектами DirectoryInfo и FileInfo.

Метод загрузки должен быть следующим:

async Task Upload(FileInfo file)
{
    var folder=file.Directory.Name;
    var blob = _cloudBlobContainer.GetBlockBlobReference(${"textures/{folder}/{file.Name}";
    if (!await blob.ExistsAsync())
    {
        await blob.UploadFromFileAsync(file.FullName);
    }
}

Запрос на создание задачи может быть упрощен до:

var tasks = from var directory in matFileDirectories
            let dir=new DirectoryInfo(directory)
            from file in dir.EnumerateFiles()
            select UploadFile(file);

await Task.WhenAll(tasks);

Это попытается отключить все операции загрузкитак быстро, как файлы могут быть повторены. Это может затопить сеть. Одним из решений является использование ActionBlock , который будет использовать только 8 задач одновременно для загрузки файлов. Ограничение также накладывается на входной буфер, чтобы избежать его заполнения, например, 1000 элементами FileInfo:

var options=new ExecutionDataflowBlockOptions
      {
         MaxDegreeOfParallelism = 8,  //Only 8 concurrent operations
         BoundedCapacity=64           //Block posters if the input buffer has too many items
      } ;
var block=new ActionBlock<FileInfo>(async file=>UploadFile(file),options);

var files = from var directory in matFileDirectories
            let dir=new DirectoryInfo(directory)
            from file in dir.EnumerateFiles()
            select file;

foreach(var file in files)
{
    //Wait here if the input buffer is full
    await block.SendAsync(file);
}

block.Complete();

//Wait for all uploads to finish
await block.Completion;
...