并行化如何在异步/等待中工作?

时间:2019-06-30 21:03:26

标签: c# .net asynchronous asp.net-core async-await

我有以下代码,打算异步运行。我的目标是根据需要并行调用GetPictureForEmployeeAsync()。我想确保CreatePicture上的“ await”不会阻止我这样做。

    public Task<Picture[]> GetPictures(IDictionary<string, string> tags)
    {
        var query = documentRepository.GetRepositoryQuery();

        var employees = query.Where(doc => doc.Gender == tags["gender"]);

        return Task.WhenAll(employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)));
    }

    private Task<Picture> GetPictureForEmployeeAsync(Employee employee, IDictionary<string, string> tags)
    {
        var base64PictureTask = blobRepository.GetBase64PictureAsync(employee.ID.ToString());
        var documentTask = documentRepository.GetItemAsync(employee.ID.ToString());
        return CreatePicture(tags, base64PictureTask, documentTask);
    }

    private static async Task<Picture> CreatePicture(IDictionary<string, string> tags, Task<string> base64PictureTask, Task<Employee> documentTask)
    {
        var document = await documentTask;

        return new Picture
        {
            EmployeeID = document.ID,
            Data = await base64PictureTask,
            ID = document.ID.ToString(),
            Tags = tags,
        };
    }

如果我正确理解,Task.WhenAll()不会受到CreatePicture()中两个等待的任务的影响,因为没有等待GetPictureForEmployeeAsync()。我对吗?如果没有,我应该如何重组代码以实现我想要的?

2 个答案:

答案 0 :(得分:3)

  

我想确保CreatePicture上的“ await”不会阻止我这样做。

不是。

  

如果我正确理解,则Task.WhenAll()不受CreatePicture()中两个等待的任务影响,因为未等待GetPictureForEmployeeAsync()。我对吗?

是,不是。 WhenAll不受CreatePicture中等待的任务的任何限制,但是与是否等待GetPictureForEmployeeAsync无关。就行为而言,这两行代码是等效的:

return Task.WhenAll(employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)));
return Task.WhenAll(employees.Select(async employee => await GetPictureForEmployeeAsync(employee, tags)));

我建议您阅读async intro,以更好地了解asyncawait如何处理任务。

此外,由于GetPictures具有非平凡的逻辑(GetRepositoryQuery并求值tags["gender"]),因此我recommend using async and await表示GetPictures,如下:

public async Task<Picture[]> GetPictures(IDictionary<string, string> tags)
{
  var query = documentRepository.GetRepositoryQuery();
  var employees = query.Where(doc => doc.Gender == tags["gender"]);
  var tasks = employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)).ToList();

  return await Task.WhenAll(tasks);
}

最后一点,如果您不传递“本来就该等待的任务”,则可以找到代码清理器,而是先await个并传递它们的结果值:

async Task<Picture> GetPictureForEmployeeAsync(Employee employee, IDictionary<string, string> tags)
{
  var base64PictureTask = blobRepository.GetBase64PictureAsync(employee.ID.ToString());
  var documentTask = documentRepository.GetItemAsync(employee.ID.ToString());
  await Task.WhenAll(base64PictureTask, documentTask);
  return CreatePicture(tags, await base64PictureTask, await documentTask);
}

static Picture CreatePicture(IDictionary<string, string> tags, string base64Picture, Employee document)
{
  return new Picture
  {
    EmployeeID = document.ID,
    Data = base64Picture,
    ID = document.ID.ToString(),
    Tags = tags,
  };
}

答案 1 :(得分:1)

调用异步方法时要记住的一点是,一旦在该方法中到达await语句,控件立即返回到调用该方法的代码。异步方法-无论await语句恰好位于方法中的任何位置。使用“普通”方法,控制不会返回到调用该方法的代码,直到到达该方法的末尾。

因此,您可以执行以下操作:

private async Task<Picture> GetPictureForEmployeeAsync(Employee employee, IDictionary<string, string> tags)
    {
        // As soon as we get here, control immediately goes back to the GetPictures
        //   method -- no need to store the task in a variable and await it within
        //   CreatePicture as you were doing
        var picture = await blobRepository.GetBase64PictureAsync(employee.ID.ToString());
        var document = await documentRepository.GetItemAsync(employee.ID.ToString());
        return CreatePicture(tags, picture, document);
    }

由于GetPictureForEmployeeAsync的第一行代码有一个await,因此控件将立即回到该行...

return Task.WhenAll(employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)));

...只要被调用。这将影响所有员工项目的并行处理(嗯,分配给您的应用程序的线程数将受到限制)。

另一个建议是,如果此应用程序正在访问数据库或Web服务以获取图片或文档,则此代码可能会导致可用连接用尽的问题。如果是这种情况,请考虑使用System.Threading.Tasks.Parallel并设置最大并行度,或使用SemaphoreSlim控制同时使用的连接数。