C# 异步/等待并行化是如何工作的?

C# 异步/等待并行化是如何工作的?,c#,.net,asynchronous,asp.net-core,async-await,C#,.net,Asynchronous,Asp.net Core,Async Await,我准备异步运行以下代码。我的目标是根据需要并行调用GetPictureForEmployeeAsync()。我想确保CreatePicture上的“等待”不会阻止我这样做 public Task<Picture[]> GetPictures(IDictionary<string, string> tags) { var query = documentRepository.GetRepositoryQuery(); var

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

    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,
        };
    }
public任务GetPictures(IDictionary标记)
{
var query=documentRepository.GetRepositoryQuery();
var employees=query.Where(doc=>doc.Gender==标记[“Gender”]);
返回Task.WhenAll(employee.Select(employee=>GetPictureForEmployeeAsync(employee,tags));
}
私有任务GetPictureForEmployeeAsync(员工,IDictionary标记)
{
var base64PictureTask=blobRepository.GetBase64PictureAsync(employee.ID.ToString());
var documentTask=documentRepository.GetItemAsync(employee.ID.ToString());
返回CreatePicture(标签、base64PictureTask、documentTask);
}
专用静态异步任务CreatePicture(IDictionary标记、Task base64PictureTask、Task documentTask)
{
var document=等待documentTask;
返回新图片
{
EmployeeID=document.ID,
数据=等待base64PictureTask,
ID=document.ID.ToString(),
标签=标签,
};
}

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

调用异步方法时要记住的一点是,只要在该方法中到达
wait
语句,control立即返回调用异步方法的代码,不管await语句在方法中的什么位置。对于“普通”方法,控件不会返回调用该方法的代码,直到到达该方法的末尾

因此,在您的情况下,您可以执行以下操作:

private异步任务GetPictureForEmployeeAsync(雇员-雇员,IDictionary标记)
{
//我们一到这里,控制就立即返回到GetPictures
//方法--不需要将任务存储在变量中并在变量中等待它
//按您所做的方式创建图片
var picture=await blobRepository.GetBase64PictureAsync(employee.ID.ToString());
var document=await documentRepository.GetItemAsync(employee.ID.ToString());
返回CreatePicture(标签、图片、文档);
}
因为GetPictureForEmployeeAsync中的第一行代码有一个
wait
,所以控件将立即返回这一行

returntask.WhenAll(employees.Select(employee=>GetPictureForEmployeeAsync(employees,tags));
…一旦被调用。这将产生并行处理所有员工项目的效果(嗯,有点——分配给应用程序的线程数量是有限的)

另外一条建议是,如果此应用程序正在访问数据库或web服务以获取图片或文档,则此代码可能会导致您出现可用连接不足的问题。如果是这种情况,考虑使用<代码>系统。线程。任务。并行< /代码>并设置最大并行度,或者使用<代码> SimaPraceSimLime/Cult>来控制同时使用的连接数量。

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

    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()未被等待。我说的对吗

是和否。
whalll
不受
CreatePicture
中等待的任务的任何限制,但这与是否等待
GetPictureForEmployeeAsync
无关。这两行代码在行为方面是等效的:

return Task.WhenAll(employees.Select(employee => GetPictureForEmployeeAsync(employee, tags)));
return Task.WhenAll(employees.Select(async employee => await GetPictureForEmployeeAsync(employee, tags)));
我建议阅读我的,以便更好地了解
async
wait
如何处理任务

此外,由于
GetPictures
具有非平凡的逻辑(
GetRepositoryQuery
和评估
标记[“性别”]
),因此
GetPictures
的I为:

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

“Task.WhenAll不受影响”--以何种方式受影响?当每个等待的任务完成时,传递给
WhenAll()
的每个任务最终都会完成,从这个意义上说,这肯定会受到影响。您的实现似乎是合理的(尽管存在编译时错误);
GetPictureForEmployee()
方法返回由
CreatePicture()
返回的任务,通过
Select()
将输入数据投影到这些任务,您可以创建许多这样的任务,然后异步等待所有这样的任务完成,最终,你问的问题并不十分清楚。当异步/等待操作正确时,并行化可以很好地工作,反之则很差。就像其他事情一样。Async/await本身并不是真正的并行化。它可以在这样的环境中使用,但对于任何异步完成的工作来说,它实际上是一个更一般的概念,无论是涉及到一些工作集合的并发处理还是单个异步操作。这看起来像是TPL数据流的情况,或者RXI建议您遵循使用
Async
作为任务返回方法名称后缀的惯例。如果您需要限制并行执行而不诉诸外来解决方案(TPL数据流、反应式扩展),这两个链接可能会很有帮助:,请注意,您提出的替代方案具有显著的,坦率地说,它改变了OP代码的语义。特别是,虽然原始代码是根据