簡體   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