Compartilhar via


Crie dependências de tarefas para executar tarefas que dependam de outras tarefas

Com dependências entre tarefas do Lote, você cria tarefas que estão agendadas para execução em nós de computação após a conclusão de uma ou mais tarefas pai. Por exemplo, você pode criar um trabalho que processa cada quadro de um filme 3D com tarefas paralelas separadas. Só depois que todos os quadros renderizados são gerados com êxito, a tarefa final os mescla no filme completo. Em outras palavras, a tarefa final depende das tarefas pai anteriores.

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

  • Cargas de trabalho de estilo MapReduce na nuvem.
  • Trabalhos cujas tarefas de processamento de dados podem ser expressas como um DAG (grafo direcionado acíclico).
  • Processos de pré-renderização e pós-renderização, em que cada tarefa deve ser concluída antes do início da próxima.
  • Qualquer outro trabalho no qual tarefas downstream dependem da saída das tarefas upstream.

Por padrão, as tarefas dependentes estão agendadas para execução somente após a conclusão bem-sucedida da tarefa pai. Opcionalmente, especifique uma ação de dependência para substituir o comportamento padrão e executar a tarefa dependente mesmo que a tarefa pai falhe.

Neste artigo, discutimos como configurar dependências de tarefas usando a biblioteca .NET do Lote. Primeiro mostramos como habilitar a dependência de tarefa nos trabalhos. Em seguida, demonstramos brevemente como configurar uma tarefa com dependências. Também descrevemos como especificar uma ação de dependência para executar tarefas dependentes em caso de falha do pai. Finalmente, discutiremos os cenários de dependência aos quais o Lote dá suporte.

Habilitar dependências de tarefas

Para usar dependências entre tarefas no aplicativo do Lote, é necessário primeiro configurar o trabalho para usar dependências entre tarefas. No .NET do Lote, habilite-o no CloudJob configurando a 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 snippet de código anterior, "batchClient" é uma instância da classe BatchClient.

Criar tarefas dependentes

Para criar uma tarefa que depende da conclusão de uma ou mais tarefas pai, é possível especificar que a tarefa “depende” das outras tarefas. No .NET do Lote, 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 snippet de código cria uma tarefa dependente com a identificação da tarefa “Flowers”. A tarefa “Flowers” depende das tarefas “Rain” e “Sun”. A tarefa “Flowers” será agendada para execução em um nó de computação somente após a conclusão bem-sucedida das tarefas “Rain” e “Sun”.

Observação

Por padrão, uma tarefa é considerada concluída com êxito quando está no estado concluído e seu código de saída é 0. No .NET do Lote, isso significa que o valor da propriedade CloudTask.State é Completed e o valor da propriedade TaskExecutionInformation.ExitCode de CloudTask é 0. Para saber como alterar isso, veja a seção Ações de dependência.

Cenários de dependência

Há três cenários de dependência de tarefas básicos que você pode usar no Lote do Azure: um-para-um, um-para-muitos e dependência de intervalo de IDs de tarefas. Esses três cenários podem ser combinados para fornecer um quarto cenário, muitos-para-muitos.

Cenário Exemplo Ilustração
Um-para-um taskB depende de taskA

taskB não será agendada para execução até que taskA tenha sido concluída com êxito

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

taskC não ser agendada para execução até que taskA e taskB tenham sido concluídos com êxito

Diagrama mostrando o cenário de dependência de tarefa um-para-muitos.
Intervalo de IDs de tarefa taskD depende de um intervalo de tarefas

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

Diagrama mostrando o cenário de dependência de tarefa do intervalo de ID da tarefa.

Dica

Você pode criar relações muitos para muitos, como uma em que as tarefas C, D, E e F dependem das tarefas A e B. Isso é útil, por exemplo, em cenários de pré-processamento em paralelo em que as tarefas downstream dependem da saída de várias tarefas upstream.

Nos exemplos desta seção, uma tarefa dependente é executada somente após a conclusão bem-sucedida das tarefas pai. Esse comportamento é o comportamento padrão de uma tarefa dependente. É possível executar uma tarefa dependente após uma falha na tarefa pai especificando uma ação de dependência para substituir o comportamento padrão.

Um para um

Em uma relação um-para-um, uma tarefa depende da conclusão bem-sucedida de uma tarefa pai. Para criar a dependência, forneça uma única identificação da tarefa para o método estático TaskDependencies.OnId ao 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

Em uma relação um-para-muitos, uma tarefa depende da conclusão de várias tarefas pai. Para criar a dependência, forneça uma coleção de IDs de tarefa específicas para o método estático TaskDependencies.OnIds ao 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 das IDs de tarefa pai for maior que 64.000 caracteres. Para especificar um grande número de tarefas pai, considere usar um intervalo de IDs de tarefa.

Intervalo de IDs de tarefa

No caso da dependência em um intervalo de tarefas pai, uma tarefa depende da conclusão das tarefas cujas IDs estão no intervalo especificado.

Para criar a dependência, forneça ao método estático TaskDependencies.OnIdRange a primeira e a última ID de tarefa no intervalo quando preencher a propriedade CloudTask.DependsOn.

Importante

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

Zeros à esquerda não são significativos ao avaliar as dependências do intervalo. Portanto, as tarefas com identificadores de cadeia de caracteres 4, 04 e 004 estarão dentro do intervalo e serão todas tratadas como uma tarefa 4. Assim, a primeira a ser concluída atenderá à dependência.

Para a execução da tarefa dependente, cada tarefa no intervalo deve atender à dependência, sendo concluída com êxito ou com uma falha mapeada para uma ação de dependência definida como Atender.

// 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 padrão, uma tarefa dependente ou um conjunto de tarefas é executado somente após a conclusão bem-sucedida de uma tarefa pai. Em alguns cenários, talvez você deseje executar tarefas dependentes mesmo se houver uma falha da tarefa pai. Você pode substituir o comportamento padrão especificando uma ação de dependência que indica se uma tarefa dependente está qualificada para ser executada.

Por exemplo, suponha que uma tarefa dependente está aguardando dados da conclusão da tarefa upstream. Se a tarefa upstream falhar, a tarefa dependente ainda poderá ser executada usando dados mais antigos. Nesse caso, uma ação de dependência pode especificar que a tarefa dependente é qualificada para execução, apesar da falha da tarefa pai.

Uma ação de dependência baseia-se em uma condição de saída da tarefa pai. Você 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 arquivo. Se a tarefa é encerrada com um código de saída especificado por meio de exitCodes ou exitCodeRanges, e, em seguida, encontra erro de carregamento de arquivo, a ação especificada pelo código de saída tem precedência.
  • Quando a tarefa é encerrada com um código de saída definido pela propriedade ExitCodes.
  • Quando a tarefa é encerrada com um código de saída que está dentro de um intervalo especificado pela propriedade ExitCodeRanges.
  • No caso padrão, se a tarefa for encerrada com um código de saída não definido por ExitCodes ou ExitCodeRanges, ou se a tarefa for encerrada com um erro de pré-processamento e a propriedade PreProcessingError não for definida, ou se houver erro de carregamento de arquivo e a propriedade FileUploadError não estiver definida.

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

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

  • Atender: indica que as tarefas dependentes estarão qualificadas para execução se a tarefa pai for encerrada com um erro especificado.
  • Bloquear: indica que as tarefas dependentes não estarão qualificadas para execução.

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

O snippet de código a seguir define a propriedade DependencyAction de uma tarefa pai. Se a tarefa pai é encerrada com um erro de pré-processamento ou com os códigos de erro especificados, a tarefa dependente é bloqueada. Se a tarefa pai é encerrada com qualquer outro erro diferente de zero, a tarefa dependente é qualificada para execução.

// 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 habilitar a dependência entre tarefas em um trabalho.
  • Como criar tarefas que dependem de outras tarefas.
  • Como executar essas tarefas em um pool de nós de computação.

Próximas etapas