Criar dependências de tarefas para executar tarefas que dependem de outras tarefas

Com as dependências de tarefas do Batch, cria tarefas agendadas para execução em nós de computação após a conclusão de uma ou mais tarefas principais. Por exemplo, pode criar uma tarefa que compõe cada frame de um filme 3D com tarefas paralelas separadas. A tarefa final intercala os frames compostos no filme completo apenas depois de todos os fotogramas terem sido compostos com êxito. Por outras palavras, a tarefa final depende das tarefas principais anteriores.

Alguns cenários em que as dependências de tarefas são úteis incluem:

  • Cargas de trabalho do estilo MapReduce na cloud.
  • Tarefas cujas tarefas de processamento de dados podem ser expressas como um grafo acíclico direcionado (DAG).
  • Processos de pré-composição e pós-composição, em que cada tarefa tem de ser concluída antes de a tarefa seguinte poder começar.
  • Qualquer outro trabalho em que as tarefas a jusante dependam da saída de tarefas a montante.

Por predefinição, as tarefas dependentes são agendadas para execução apenas depois de a tarefa principal ter sido concluída com êxito. Opcionalmente, pode especificar uma ação de dependência para substituir o comportamento predefinido e executar a tarefa dependente, mesmo que a tarefa principal falhe.

Neste artigo, vamos falar sobre como configurar dependências de tarefas com a biblioteca .NET do Batch . Primeiro, mostramos como ativar a dependência de tarefas nos seus trabalhos e, em seguida, demonstramos como configurar uma tarefa com dependências. Também descrevemos como especificar uma ação de dependência para executar tarefas dependentes se o principal falhar. Por fim, abordamos os cenários de dependência suportados pelo Batch.

Ativar dependências de tarefas

Para utilizar dependências de tarefas na sua aplicação do Batch, primeiro tem de configurar a tarefa para utilizar dependências de tarefas. No Batch .NET, ative-o no CloudJob ao definir a respetiva propriedade UsesTaskDependencies como true:

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

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

No fragmento de código anterior, "batchClient" é uma instância da classe BatchClient .

Criar tarefas dependentes

Para criar uma tarefa que dependa da conclusão de uma ou mais tarefas principais, pode especificar que a tarefa "depende" das outras tarefas. No Batch .NET, configure a propriedade CloudTask.DependsOn com uma instância da classe 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")
},

Este fragmento de código cria uma tarefa dependente com o ID da tarefa "Flores". A tarefa "Flores" depende das tarefas "Chuva" e "Sol". A tarefa "Flores" será agendada para ser executada num nó de computação apenas depois de as tarefas "Chuva" e "Sol" terem sido concluídas com êxito.

Nota

Por predefinição, considera-se que uma tarefa foi concluída com êxito quando está no estado concluído e o respetivo código de saída é 0. No Batch .NET, isto significa que um valor de propriedade CloudTask.State é Completed e o valor da propriedade TaskExecutionInformation.ExitCode da CloudTask é 0. Para saber como alterar isto, veja a secção Ações de dependência .

Cenários de dependência

Existem três cenários básicos de dependência de tarefas que pode utilizar no Azure Batch: dependência de um-para-um, um-para-muitos e intervalo de ID de tarefa. Estes três cenários podem ser combinados para fornecer um quarto cenário: muitos para muitos.

Scenario Exemplo Ilustração
Um para um taskB depende da tarefa Uma

tarefaB não será agendada para execução até que a tarefaA seja concluída com êxito

Diagrama a mostrar o cenário de dependência de tarefa um-para-um.
Um-para-muitos taskC depende de taskA e taskB

taskC não será agendado para execução até que a tarefaA e a tarefaB tenham sido concluídas com êxito

Diagrama a mostrar o cenário de dependência de tarefa um-para-muitos.
Intervalo do ID da Tarefa taskD depende de um intervalo de tarefas

taskD não será agendado para execução até que as tarefas com IDs 1 a 10 tenham sido concluídas com êxito

Diagrama a mostrar o cenário de dependência de tarefas do intervalo de IDs de tarefas.

Dica

Pode criar relações muitos-para-muitos , como, por exemplo, onde as tarefas C, D, E e F dependem das tarefas A e B. Isto é útil, por exemplo, em cenários de pré-processamento paralelos em que as tarefas a jusante dependem da saída de várias tarefas a montante.

Nos exemplos desta secção, uma tarefa dependente só é executada depois de concluídas com êxito as tarefas principais. Este comportamento é o comportamento predefinido de uma tarefa dependente. Pode executar uma tarefa dependente depois de uma tarefa principal falhar ao especificar uma ação de dependência para substituir o comportamento predefinido.

Um para um

Numa relação um-para-um, uma tarefa depende da conclusão bem-sucedida de uma tarefa principal. Para criar a dependência, forneça um único ID de tarefa para o método estático TaskDependencies.OnId quando preencher a propriedade CloudTask.DependsOn .

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

Um-para-muitos

Numa relação um-para-muitos, uma tarefa depende da conclusão de várias tarefas principais. Para criar a dependência, forneça uma coleção de IDs de tarefa específicos para o método estático TaskDependencies.OnIds quando preencher a propriedade CloudTask.DependsOn .

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

Importante

A criação da tarefa dependente falhará se o comprimento combinado dos IDs de tarefa principal for superior a 64000 carateres. Para especificar um grande número de tarefas principais, considere utilizar antes um intervalo de ID de Tarefa.

Intervalo do ID da Tarefa

Numa dependência de um intervalo de tarefas principais, uma tarefa depende da conclusão de tarefas cujos IDs estão dentro de um intervalo que especificar.

Para criar a dependência, forneça os IDs da primeira e última tarefa no intervalo para o método estático TaskDependencies.OnIdRange quando preencher a propriedade CloudTask.DependsOn .

Importante

Quando utiliza intervalos de ID de tarefa para as suas dependências, apenas as tarefas com IDs que representam valores inteiros serão selecionadas pelo intervalo. Por exemplo, o intervalo 1..10 irá selecionar tarefas 3 e 7, mas não 5flamingoes.

Os zeros à esquerda não são significativos na avaliação das dependências do intervalo, pelo que as tarefas com identificadores de cadeia de carateres 4e 04004 estarão todas dentro do intervalo, uma vez que serão todas tratadas como tarefas 4, a primeira a concluir irá satisfazer a dependência.

Para que a tarefa dependente seja executada, todas as tarefas no intervalo têm de satisfazer a dependência, seja ao concluir com êxito ou ao concluir com uma falha mapeada para uma ação de dependência definida como Satisfazer.

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

Ações de dependência

Por predefinição, uma tarefa dependente ou um conjunto de tarefas só é executado depois de uma tarefa principal ter sido concluída com êxito. Em alguns cenários, poderá querer executar tarefas dependentes mesmo que a tarefa principal falhe. Pode substituir o comportamento predefinido ao especificar uma ação de dependência que indica se uma tarefa dependente é elegível para ser executada.

Por exemplo, suponha que uma tarefa dependente está à espera de dados da conclusão da tarefa a montante. Se a tarefa a montante falhar, a tarefa dependente ainda poderá ser executada com dados mais antigos. Neste caso, uma ação de dependência pode especificar que a tarefa dependente é elegível para ser executada, apesar da falha da tarefa principal.

Uma ação de dependência baseia-se numa condição de saída para a tarefa principal. Pode especificar uma ação de dependência para qualquer uma das seguintes condições de saída:

  • Quando ocorre um erro de pré-processamento.
  • Quando ocorre um erro de carregamento de ficheiros. Se a tarefa sair com um código de saída especificado através de exitCodes ou exitCodeRanges e, em seguida, encontrar um erro de carregamento de ficheiros, a ação especificada pelo código de saída tem precedência.
  • Quando a tarefa sai com um código de saída definido pela propriedade ExitCodes .
  • Quando a tarefa sai com um código de saída que se insere num intervalo especificado pela propriedade ExitCodeRanges .
  • O caso predefinido, se a tarefa sair com um código de saída não definido por ExitCodes ou ExitCodeRanges, ou se a tarefa sair com um erro de pré-processamento e a propriedade PreProcessingError não estiver definida ou se a tarefa falhar com um erro de carregamento de ficheiros e a propriedade FileUploadError não estiver definida.

Para .NET, estas condições são definidas como propriedades da classe ExitConditions .

Para especificar uma ação de dependência, defina a propriedade ExitOptions.DependencyAction para a condição de saída como uma das seguintes:

  • Satisfazer: indica que as tarefas dependentes são elegíveis para execução se a tarefa principal sair com um erro especificado.
  • Bloco: indica que as tarefas dependentes não são elegíveis para execução.

A predefinição para a propriedade DependencyAction é Satisfazer para o código de saída 0 e Bloquear para todas as outras condições de saída.

O fragmento de código seguinte define a propriedade DependencyAction para uma tarefa principal. Se a tarefa principal sair com um erro de pré-processamento ou com os códigos de erro especificados, a tarefa dependente será bloqueada. Se a tarefa principal sair com qualquer outro erro que não seja zero, a tarefa dependente é elegível para ser executada.

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

Exemplo de código

O projeto de exemplo TaskDependencies no GitHub demonstra:

  • Como ativar a dependência de tarefas numa tarefa.
  • Como criar tarefas que dependem de outras tarefas.
  • Como executar essas tarefas num conjunto de nós de computação.

Passos seguintes