最初的方法是低效的foreach循环,等待每个作业(job)(这是一个I/O绑定的网络调用):

foreach (var job in Jobs)
{
    try
    {
        await DoJobAsync(job); //Pass job to external vendor API
        job.Succeeded = true;
    }
    
    catch (Exception)
    {
        //do nothing, let loop continue
    }
}

现在为了提高性能,我希望使用Task.WhenAll以非阻塞方式处理所有作业(job).

但是,如果DoJobAsync任务没有引发异常,我们需要确保每个job对象只有Succeeded属性设置为true.

如果我们这样做:

await Task.WhenAll(Jobs.Select(j =>
{
    var task = DoJobAsync(j);
    j.Succeeded = true;
    return task;
}));

我的理解是,如果任何作业(job)任务最终抛出异常,该属性仍将切换为true,因 for each 单独的任务在创建时都不会等待,从而使代码流直接经过它.

我知道我可以捕获Task.WhenAll返回的Task,以访问抛出的所有异常的列表,但我无法找到使用它们追溯到抛出异常的job的方法.

我如何解决这个问题?

推荐答案

您可以使用异步委托作为Select运算符的selector:

await Task.WhenAll(Jobs.Select(async job =>
{
    await DoJobAsync(job);
    job.Succeeded = true;
}));

这样,每个作业(job)将被投影到Task,而不是原始的DoJobAsync(job)任务,而是封装更新Succeeded属性的逻辑的包装器任务.此属性将在DoJobAsync(job)任务成功完成后立即更新.

多个Job对象可能会并行更新其Succeeded属性.这取决于当前线程上是否安装了SynchronizationContext.

.net相关问答推荐

实体框架核心:Azure容器应用程序的执行超时已过期

等待时 Blazor 服务器按钮刷新

"投掷;" 是什么意思?靠自己做什么?

将 Moq 模拟对象传递给构造函数

我在哪里可以获得线程安全的 CollectionView?

根源是什么?

如何中止任务,如中止线程(Thread.Abort 方法)?

如何通过 LINQ 比较没有时间的 DateTime?

msbuild,定义条件编译符号

支持 HTTPS 的 Httplistener

C# 的 Actors 有什么好的实现吗?

软件包版本始终为 1.0.0,带有 dotnet pack

我不了解应用程序域

如何保护我的 .NET 程序集免受反编译?

在 .NET 中计算目录大小的最佳方法是什么?

如何在 C# 中以编程方式安装 Windows 服务?

C# 中的 override 和 new 关键字有什么区别?

泛型类的默认构造函数的语法是什么?

将控制台输出镜像到文件

System.Array.CopyTo() 和 System.Array.Clone() 之间的区别