创建任务依赖关系,以运行依赖于其他任务的任务

使用 Batch 任务依赖关系,可以创建在完成一个或多个父任务后在计算节点上按计划执行的任务。 例如,可以创建一个作业,使用单独的并行任务渲染 3D 影片的每个帧。 最后一个任务仅在所有帧已成功渲染后,才将渲染的帧合并为完整影片。 换句话说,最后一个任务依赖于前面的父任务。

任务依赖关系可发挥作用的部分方案包括:

  • 云中的 MapReduce 样式工作负荷。
  • 数据处理任务可以表示为有向无环图 (DAG) 的作业。
  • 预呈现和后期呈现过程,其中每个任务必须在下一个任务开始之前完成。
  • 下游任务依赖于上游任务输出的任何其他作业。

默认情况下,依赖任务计划为仅在成功完成父任务后执行。 你可以选择指定一个依赖关系操作来替代默认行为,并在父任务失败时运行依赖任务。

本文讨论如何使用 Batch .NET 库配置任务依赖关系。 本文首先说明如何为作业启用任务依赖关系,然后演示如何为任务配置依赖关系。 本文还介绍如何指定一个依赖关系操作,以便在父任务失败时运行依赖任务。 最后介绍 Batch 支持的依赖关系方案

启用任务依赖关系

要在批处理应用程序中使用任务依赖关系,必须先将作业配置为使用任务依赖关系。 在 Batch .NET 中,为 CloudJob 启用任务依赖关系的方法是将其 UsesTaskDependencies 属性设置为 true

CloudJob unboundJob = batchClient.JobOperations.CreateJob( "job001",
    new PoolInformation { PoolId = "pool001" });

// IMPORTANT: This is REQUIRED for using task dependencies.
unboundJob.UsesTaskDependencies = true;

在以上代码片段中,“batchClient”是 BatchClient 类的一个实例。

创建依赖任务

若要创建一个依赖于一个或多个父任务的完成的任务,可以指定该任务必须“依赖于”其他任务。 在 Batch .NET 中,为 CloudTask.DependsOn 属性配置 TaskDependencies 类的一个实例:

// Task 'Flowers' depends on completion of both 'Rain' and 'Sun'
// before it is run.
new CloudTask("Flowers", "cmd.exe /c echo Flowers")
{
    DependsOn = TaskDependencies.OnIds("Rain", "Sun")
},

此代码片段创建任务 ID 为“Flowers”的依赖任务。 “Flowers”任务依赖于“Rain”和“Sun”任务。 任务“Flowers”将在任务“Rain”和“Sun”成功完成后才会安排在计算节点上运行。

备注

默认情况下,当任务处于已完成状态并且其退出代码为 0 时,该任务被视为已成功完成。 在 Batch .NET 中,这意味着 CloudTask.State 属性值为 Completed,并且 CloudTask 的 TaskExecutionInformation.ExitCode 属性值为 0。 若要了解如何更改此设置,请参阅依赖关系操作部分。

依赖关系方案

可以在 Azure Batch 中使用三种基本任务依赖关系方案:一对一、一对多和任务 ID 范围依赖关系。 可以组合这三种方案来提供第四种方案:多对多。

场景 示例 图示
一对一 taskB 依赖于 taskA

只有在 taskA 成功完成后,系统才会按计划执行 taskB

示意图中显示了一对一任务依赖关系方案。
一对多 taskC 依赖于 taskA 和 taskB

taskC 不会被安排执行,直到 taskAtaskB 都成功完成

示意图中显示了一对多任务依赖关系方案。
任务 ID 范围 taskD 依赖于一系列任务,这些任务的 ID 从 110taskD 在这些任务成功完成之前,不会被安排执行。 示意图中显示了任务 ID 范围任务依赖关系方案。

提示

可以创建 多对多 关系,例如任务 C、D、E 和 F 分别依赖于任务 A 和 B。例如,在并行化的预处理方案中,下游任务依赖于多个上游任务的输出,这非常有用。

在本部分的示例中,仅在父任务成功完成时才运行依赖任务。 这是依赖任务的默认行为。 你可以通过指定一个依赖关系操作来替代默认行为,在父任务失败后运行依赖项任务。

一对一

在一对一关系中,任务依赖于一个父任务的成功完成。 若要创建该依赖关系,请在填充 CloudTask.DependsOn 属性时,为 TaskDependencies.OnId 静态方法提供单个任务 ID。

// Task 'taskA' doesn't depend on any other tasks
new CloudTask("taskA", "cmd.exe /c echo taskA"),

// Task 'taskB' depends on completion of task 'taskA'
new CloudTask("taskB", "cmd.exe /c echo taskB")
{
    DependsOn = TaskDependencies.OnId("taskA")
},

一对多

在一对多关系中,任务依赖于多个父任务的完成。 若要创建该依赖关系,请在填充 CloudTask.DependsOn 属性时,为 TaskDependencies.OnIds 静态方法提供特定任务 ID 的集合。

// 'Rain' and 'Sun' don't depend on any other tasks
new CloudTask("Rain", "cmd.exe /c echo Rain"),
new CloudTask("Sun", "cmd.exe /c echo Sun"),

// Task 'Flowers' depends on completion of both 'Rain' and 'Sun'
// before it is run.
new CloudTask("Flowers", "cmd.exe /c echo Flowers")
{
    DependsOn = TaskDependencies.OnIds("Rain", "Sun")
},

重要

如果父任务 ID 的组合长度大于 64,000 个字符,则依赖任务创建将失败。 若要指定大量的父任务,请考虑改为使用任务 ID 范围。

任务 ID 范围

在依赖于一系列父任务的关系中,任务依赖于其 ID 位于指定的范围内的任务的完成情况。

若要创建该依赖关系,请在填充 CloudTask.DependsOn 属性时,为 TaskDependencies.OnIdRange 静态方法提供该范围内的第一个和最后一个任务 ID。

重要

对依赖项使用任务 ID 范围时,范围只选择了表示整数值 ID 的任务。 例如,范围 1..10 选择了任务 37,但未选择 5flamingoes

计算范围依赖项时,前导零并不重要,因此具有字符串标识符404的任务,并且004位于范围内,因为它们都被视为任务4,因此要完成的第一个任务满足依赖项。

对于要运行的依赖任务,范围内的每个任务都必须满足该依赖关系,这分为两种情况:一种情况是成功完成,另一种情况是已完成,但出现了失败,该失败映射到设置为“Satisfy”的某个依赖关系操作

// Tasks 1, 2, and 3 don't depend on any other tasks. Because
// we will be using them for a task range dependency, we must
// specify string representations of integers as their ids.
new CloudTask("1", "cmd.exe /c echo 1"),
new CloudTask("2", "cmd.exe /c echo 2"),
new CloudTask("3", "cmd.exe /c echo 3"),

// Task 4 depends on a range of tasks, 1 through 3
new CloudTask("4", "cmd.exe /c echo 4")
{
    // To use a range of tasks, their ids must be integer values.
    // Note that we pass integers as parameters to TaskIdRange,
    // but their ids (above) are string representations of the ids.
    DependsOn = TaskDependencies.OnIdRange(1, 3)
},

依赖关系操作

默认情况下,依赖任务或一组任务仅在父任务成功完成后运行。 在某些情况下,建议运行依赖任务,即使父任务失败。 你可以通过指定一个依赖关系操作来替代默认行为,该操作会指示依赖任务是否有资格运行。

例如,假设某个依赖任务正在等待完成上游任务后提供的数据。 如果上游任务失败,依赖任务仍可使用旧数据运行。 在这种情况下,依赖关系操作可以指定即使父任务失败,依赖任务也符合运行的条件。

依赖关系操作基于父任务的退出条件。 可为以下任一退出条件指定依赖关系操作:

  • 每当发生预处理错误时。
  • 每当发生文件上传错误时。 如果任务退出,并返回通过“exitCodes”或“exitCodeRanges”指定的退出代码,然后遇到文件上传错误,则优先执行退出代码指定的操作 。
  • 每当任务使用 ExitCodes 属性定义的退出代码退出时。
  • 每当任务退出时,该退出代码位于 ExitCodeRanges 属性指定的范围内。
  • 默认情况是,如果任务退出时未由 ExitCodesExitCodeRanges 定义,或者任务退出时出现预处理错误且 未设置 PreProcessingError 属性,或者任务失败并出现文件上传错误且 未设置 FileUploadError 属性。

对于 .NET,这些条件定义为 ExitConditions 类的属性。

若要指定依赖项作,请将退出条件的 ExitOptions.DependencyAction 属性设置为以下选项之一:

  • Satisfy:指示如果父任务退出并返回了指定的错误,则依赖项任务有资格运行。
  • 阻止:指示依赖任务不符合运行条件。

对于退出代码 0,DependencyAction 属性的默认设置为 Satisfy;对于其他退出条件,其默认设置为 Block

以下代码片段设置父任务的 DependencyAction 属性。 如果父任务退出并出现预处理错误或具有指定错误代码,则会阻止依赖任务。 如果父任务退出并出现任何其他非零错误,则依赖任务有资格运行。

// Task A is the parent task.
new CloudTask("A", "cmd.exe /c echo A")
{
    // Specify exit conditions for task A and their dependency actions.
    ExitConditions = new ExitConditions
    {
        // If task A exits with a pre-processing error, block any downstream tasks (in this example, task B).
        PreProcessingError = new ExitOptions
        {
            DependencyAction = DependencyAction.Block
        },
        // If task A exits with the specified error codes, block any downstream tasks (in this example, task B).
        ExitCodes = new List<ExitCodeMapping>
        {
            new ExitCodeMapping(10, new ExitOptions() { DependencyAction = DependencyAction.Block }),
            new ExitCodeMapping(20, new ExitOptions() { DependencyAction = DependencyAction.Block })
        },
        // If task A succeeds or fails with any other error, any downstream tasks become eligible to run 
        // (in this example, task B).
        Default = new ExitOptions
        {
            DependencyAction = DependencyAction.Satisfy
        }
    }
},
// Task B depends on task A. Whether it becomes eligible to run depends on how task A exits.
new CloudTask("B", "cmd.exe /c echo B")
{
    DependsOn = TaskDependencies.OnId("A")
},

代码示例

GitHub 上的 TaskDependencies 示例项目演示了:

  • 如何在作业上启用任务依赖关系。
  • 如何创建依赖于其他任务的任务。
  • 如何在计算节点池中执行这些任务。

后续步骤