简体   繁体   English

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

[英]Call async method in new task c#

Problem i'm trying to solve:我试图解决的问题:

For each directory, it exists some files, and I want to upload this to Azure.对于每个目录,它都存在一些文件,我想将其上传到 Azure。

So I want to do this: Task1 - uploading files in directory 1 to azure Task 2 - uploading files in directory 2 to azure所以我想这样做: 任务1 - 将目录 1 中的文件上传到 azure 任务 2 - 将目录 2 中的文件上传到 azure

I want to do this concurrently.我想同时执行此操作。

I have the following code:我有以下代码:

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

With this code, i get the following warning:使用此代码,我收到以下警告:

Because this call is not awaited, execution of the current method continues before the call is completed.由于不等待此调用,因此在调用完成之前继续执行当前方法。 Consider applying the 'await' operator to the result of the call.考虑将“等待”运算符应用于调用结果。

What does that mean?这意味着什么? How does this affect my code?这对我的代码有何影响?

I can remove the warning by doing like this:我可以这样做删除警告:

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

Is this the correct way to this?这是正确的方法吗?

The real problem seems to be how to process multiple files in parallel.真正的问题似乎是如何并行处理多个文件。 ProcessMatFiles returns a Task already and I'd assume it doesn't run anything heavy on the caller's thread. ProcessMatFiles已经返回了一个Task ,我假设它不会在调用者的线程上运行任何繁重的任务。 That task can be stored in the tasks list.该任务可以存储在tasks列表中。 That list can be awaited without blocking with可以等待该列表而不会阻塞

await Task.WhenAll(tasks);

A better solution would be to convert the entire loop into a LINQ query that returns the Tasks and await it.更好的解决方案是将整个循环转换为返回任务并等待它的 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);

The problem with this is that enumerating the files in a folder is expensive itself, and GetFiles() , or using EnumerateFiles().ToList() has to wait for the enumeration to finish.这样做的问题是枚举文件夹中的文件本身很昂贵,并且GetFiles()或使用EnumerateFiles().ToList()必须等待枚举完成。 It would be better if ProcessMatFiles received the DirectoryInfo object and enumerated the files in a separate thread .如果ProcessMatFiles收到 DirectoryInfo object 并在单独的线程中枚举文件会更好。

Another improvement would be to process the files one-by-one:另一个改进是逐个处理文件:

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

It's possible to improve this further if one knows what ProcessMatFiles does, eg use Dataflow blocks or Channels for throttling and using a specific number of tasks, breaking the process into multiple concurrent steps etc.如果知道ProcessMatFiles的作用,则可以进一步改进这一点,例如使用 Dataflow 块或 Channels 进行节流和使用特定数量的任务,将流程分成多个并发步骤等。

Update更新

Since this is a file upload operation, each file is a separate asynchronous operation.由于这是一个文件上传操作,每个文件都是一个单独的异步操作。 Most of the checks can be removed when working with DirectoryInfo and FileInfo objects.使用 DirectoryInfo 和 FileInfo 对象时,可以删除大多数检查。

The upload method should be just:上传方法应该只是:

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

The task-producing query can be simplified to:生成任务的查询可以简化为:

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

await Task.WhenAll(tasks);

This will try to fire off all upload operations as fast as the files can be iterated.这将尝试在文件可以迭代时尽快触发所有上传操作。 This could flood the network.这可能会淹没网络。 One solution is to use an ActionBlock that will only use eg 8 tasks at a time to upload files.一种解决方案是使用一次仅使用例如 8 个任务来上传文件的ActionBlock A limit is placed on the input buffer too, to avoid filling it with eg 1000 FileInfo items:输入缓冲区也有一个限制,以避免用例如 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