Поделиться через


Создание зависимостей для выполнения задач, которые зависят от других задач

Зависимости задач пакетной службы позволяют создавать задачи, планируемые для выполнения на вычислительных узлах после завершения одной или нескольких родительских задач. Например, можно создать задание, которое отрисовывает каждый кадр трехмерного фильма с помощью отдельных параллельных задач. Конечная задача объединяет отрисованные кадры в готовый фильм только после успешной отрисовки всех кадров. Иными словами, последняя задача зависит от предыдущих родительских задач.

Ниже описано несколько ситуаций, в которых удобно использовать зависимости задач. Их можно использовать для:

  • рабочих нагрузок MapReduce в облаке;
  • заданий, задачи обработки данных которых можно представить в виде направленного ациклического графа (DAG);
  • Процессы предварительной отрисовки и после отрисовки, в которых каждая задача должна завершиться до начала следующей задачи.
  • любых других заданий, в которых подчиненные задачи зависят от выходных данных вышестоящих задач.

По умолчанию выполнение зависимых задач планируются только после успешного завершения родительской задачи. При необходимости можно указать действие зависимости, чтобы переопределить поведение по умолчанию и выполнить зависимые задачи даже при сбое родительской задачи.

В этой статье рассматривается настройка зависимостей задач с помощью библиотеки .NET пакетной службы. Сначала в этой статье описывается, как включить зависимость задачи в заданиях, а потом рассматривается, как настроить задачу с зависимостями. Мы также опишем, как указать действие зависимости, запускающее зависимые задачи при сбое родительской задачи. Напоследок рассматриваются поддерживаемые пакетной службой сценарии использования зависимостей .

Включение зависимостей задач

Чтобы использовать зависимости задач в приложении пакетной службы, сначала необходимо настроить задание для использования зависимостей задач. В .NET пакетной службы включите зависимости задач в классе CloudJob, задав для свойства UserTaskDependencies значение true.

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

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

Приведенный выше фрагмент кода batchClient представляет собой экземпляр класса BatchClient.

Создание зависимости задач

Чтобы создать задачу, зависящую от выполнения одной или нескольких родительских задач, можно указать, что эта задача "зависит" от других задач. В .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")
},

Этот фрагмент кода создает зависимую задачу с идентификатором задачи Flowers. Задача Flowers зависит от задач Rain и Sun. Задача "Цветы" будет запланирована на выполнение на вычислительном узле только после успешного выполнения задач "Дождь" и "Солнце".

Примечание.

По умолчанию задача считается успешно завершенной, если она находится в состоянии завершения, а код выхода — 0. В .NET пакетной службы это означает, что значение свойства CloudTask.State равно Completed, а значение свойства TaskExecutionInformation.ExitCode для CloudTask равно 0. Чтобы узнать, как изменить эти значения, см. раздел Действия зависимостей.

сценарии использования зависимостей

Есть три основных сценария зависимостей задач, которые можно использовать в пакетной службе Azure: "один к одному", "один ко многим" и зависимость диапазона идентификаторов задач. Эти три сценария можно объединить, чтобы получить четвертый — "многие ко многим".

Сценарий Пример Иллюстрация
Один к одному taskB зависит от taskA

taskB не будет запланировано на выполнение до тех пор, пока задачаA не завершится успешно

Схема, на которой показан сценарий зависимостей задач
Один ко многим taskC зависит от taskA и taskB

taskC не будет запланирован для выполнения до успешного завершения taskA и taskB.

Схема, на которой показан сценарий зависимостей задач
Диапазон идентификаторов задач Задача taskD зависит от выполнения ряда задач. Она не будет запланирована для выполнения до тех пор, пока задачи с идентификаторами 110 не будут успешно завершены. Схема, на которой показан сценарий зависимостей диапазона идентификаторов задач.

Совет

Вы можете создавать отношения многие ко многим, например, задачи C, D, E и F зависят от задач A и B. Это полезно в схематах параллелизованной предварительной обработки, где последующие задачи зависят от выхода нескольких предыдущих задач.

В примерах в этом разделе зависимая задача выполняется только после успешного завершения родительской задачи. Это поведение по умолчанию для зависимой задачи. После сбоя родительской задачи можно запустить зависимую задачу, указав действие зависимости, чтобы переопределить поведение по умолчанию.

Взаимно-однозначный

При взаимосвязи "один к одному" задача зависит от успешного выполнения одной родительской задачи. Чтобы создать зависимость, при заполнении свойства CloudTask.DependsOn укажите один идентификатор задачи в статическом методе TaskDependencies.OnId.

// 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.

// '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")
},

Внимание

Создание связанной задачи не удаётся, если общая длина идентификаторов родительских задач превышает 64 000 символов. Чтобы указать большое количество родительских задач, рекомендуем использовать диапазон идентификаторов задач.

Диапазон идентификаторов задач

При зависимости от диапазона родительских задач задача зависит от выполнения задач, идентификаторы которых находятся в указанном вами диапазоне.

Чтобы создать зависимость, укажите первый и последний идентификаторы задач в статическом методе TaskDependencies.OnIdRange при заполнении свойства CloudTask.DependsOn.

Внимание

При использовании диапазонов идентификаторов задач для зависимостей выбираются только задачи с идентификаторами, представленными целыми числами. Например, диапазон 1..10 выбирает задачи 3 и 7, но не 5flamingoes.

Начальные нули не являются значительными при оценке зависимостей диапазона, поэтому задачи с идентификаторами строк 4, 04 и 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 .
  • По умолчанию, если задача завершается с кодом выхода, не определенным exitCodes или ExitCodeRanges, или если задача завершается с ошибкой предварительной обработки и свойством PreProcessingError не задано, или если задача завершается ошибкой отправки файлов, а свойство FileUploadError не задано.

Для .NET эти условия определяются как свойства класса ExitConditions.

Чтобы указать действие зависимости, задайте для условия выхода свойство ExitOptions.DependencyAction одним из следующих параметров:

  • Satisfy: зависимые задачи можно будет выполнить, если родительская задача завершится с указанной ошибкой.
  • Блок. Указывает, что зависимые задачи не могут выполняться.

По умолчанию для свойства DependencyAction заданы значение Satisfy для кода выхода 0 и значение 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")
},

Пример кода

В примере проекта TaskDependencies на сайте GitHub показано:

  • как включить зависимость задачи для задания;
  • как создать задачи, которые зависят от других задач;
  • как выполнить эти задачи в пуле вычислительных узлов.

Следующие шаги