繁体   English   中英

在新任务 c# 中调用异步方法

[英]Call async method in new task c#

我试图解决的问题:

对于每个目录,它都存在一些文件,我想将其上传到 Azure。

所以我想这样做: 任务1 - 将目录 1 中的文件上传到 azure 任务 2 - 将目录 2 中的文件上传到 azure

我想同时执行此操作。

我有以下代码:

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());

使用此代码,我收到以下警告:

由于不等待此调用,因此在调用完成之前继续执行当前方法。 考虑将“等待”运算符应用于调用结果。

这意味着什么? 这对我的代码有何影响?

我可以这样做删除警告:

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

这是正确的方法吗?

真正的问题似乎是如何并行处理多个文件。 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 object 并在单独的线程中枚举文件会更好。

另一个改进是逐个处理文件:

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

如果知道ProcessMatFiles的作用,则可以进一步改进这一点,例如使用 Dataflow 块或 Channels 进行节流和使用特定数量的任务,将流程分成多个并发步骤等。

更新

由于这是一个文件上传操作,每个文件都是一个单独的异步操作。 使用 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);

这将尝试在文件可以迭代时尽快触发所有上传操作。 这可能会淹没网络。 一种解决方案是使用一次仅使用例如 8 个任务来上传文件的ActionBlock 输入缓冲区也有一个限制,以避免用例如 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;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM