Trilha de aprendizado · Nível 13 · Tutorial 7

Gerenciar tarefas e cancelamento no asyncio

Ao concluir, você será capaz de iniciar corrotinas como tarefas concorrentes, observar seus resultados e solicitar cancelamento preservando a limpeza de recursos.

  • Nível: Avançado
  • Duração: 20 min
  • 8 passos
Gerenciar tarefas e cancelamento no asyncio

O que você vai percorrer

  1. Da corrotina à tarefa agendada Diferencie criar uma corrotina, agendá-la como tarefa e aguardar seu resultado. 2 min
  2. Agendar várias tarefas antes de aguardar Agende o conjunto de corrotinas primeiro e só então observe os resultados, permitindo que elas avancem de forma concorrente. 2 min
  3. Manter referências e observar os desfechos Guarde as tarefas que você cria e observe cada resultado ou falha antes de encerrar a corrotina coordenadora. 3 min
  4. Entender o pedido de cancelamento Entenda por que cancelar uma Task é um pedido cooperativo e quando esse pedido pode ser processado. 2 min
  5. Cancelar, aguardar e confirmar Solicite o cancelamento de uma tarefa, aguarde seu desfecho e diferencie término comum de cancelamento efetivado. 3 min
  6. Limpar recursos e preservar o cancelamento Use limpeza garantida na corrotina e deixe o cancelamento continuar até quem solicitou a parada. 3 min
  7. Acompanhar a propagação entre tarefas Identifique como uma dependência ativa de await pode encaminhar um pedido de cancelamento e onde cada corrotina deve preservar sua limpeza. 2 min
  8. Aplicação final: encerrar sem abandonar tarefas Integre agendamento concorrente, observação de resultado e falha, cancelamento e limpeza em um script local completo. 4 min

O que você vai aprender

  • Agendar corrotinas com create_task antes de aguardar seus resultados.
  • Manter referências às tarefas e observar suas conclusões ou exceções.
  • Solicitar cancelamento e aguardar sua efetivação.
  • Preservar a propagação de CancelledError após a limpeza necessária.

Antes de começar

  • Criar corrotinas com async e await

Passo 1 de 8

Da corrotina à tarefa agendada

Diferencie criar uma corrotina, agendá-la como tarefa e aguardar seu resultado.

Três momentos distintos

Chamar não é executar

Você já sabe que chamar uma função definida com async def cria um objeto corrotina: o corpo da função ainda não começa a rodar.

Para colocá-la sob controle do laço de eventos, use asyncio.create_task(corrotina). Essa chamada devolve uma Task: um objeto aguardável que acompanha a execução e, depois, seu resultado ou desfecho.

create_task agenda; ele não espera a conclusão.

Do objeto corrotina ao resultado

Diagrama com três etapas conectadas: uma chamada de corrotina cria um objeto inativo; create_task o transforma em uma tarefa agendada no laço de eventos; await na tarefa leva ao resultado após a conclusão.

Criação, agendamento e espera são operações diferentes.

Agende e mantenha a Task

Uma tarefa dentro da corrotina principal

create_task deve ser chamado enquanto há um laço de eventos em execução — por exemplo, dentro de principal. Guarde o retorno em uma variável para então aguardá-lo.

python
import asyncio


async def buscar_status() -> str:
    await asyncio.sleep(1)
    return "status recebido"


async def principal() -> None:
    corrotina = buscar_status()              # cria o objeto corrotina
    tarefa = asyncio.create_task(corrotina)  # agenda e devolve uma Task

    print("tarefa agendada")
    resultado = await tarefa                 # espera o desfecho e obtém o resultado
    print(resultado)


asyncio.run(principal())

Dica

Leia o fluxo, não a ordem dos prints

Ao chegar em create_task, a tarefa fica apta a progredir quando o laço de eventos tiver oportunidade. A linha seguinte não prova que ela já terminou. Somente await tarefa espera seu desfecho e entrega o valor retornado por buscar_status.

Associe cada operação ao efeito

Criação, agendamento ou espera?

Relacione cada operação ao efeito principal.

Toque em um item e depois no par correspondente.

Passo 2 de 8

Agendar várias tarefas antes de aguardar

Agende o conjunto de corrotinas primeiro e só então observe os resultados, permitindo que elas avancem de forma concorrente.

Duas formas de organizar a espera

Agendar não é aguardar

Depois de criar uma Task com asyncio.create_task(...), ela pode progredir quando o laço de eventos tiver oportunidade. Se você cria uma tarefa e já faz await nela antes de criar a próxima, a segunda ainda nem foi agendada.

Para iniciar operações concorrentes, crie as Tasks do conjunto primeiro. Em seguida, aguarde as referências que você guardou.

Comparação de linhas do tempo

A diferença está no momento em que cada operação entra no laço de eventos.

Comparação entre uma linha do tempo sequencial, em que a segunda tarefa começa somente após a primeira ser aguardada, e uma linha do tempo concorrente, em que as duas tarefas são agendadas antes das esperas e avançam em faixas paralelas.

À esquerda, cada espera adia o agendamento seguinte. À direita, as duas Tasks já existem antes da primeira observação de resultado.

Primeiro crie as Tasks

Duas operações agendadas

Cada chamada de create_task devolve imediatamente uma referência à Task agendada.

python
import asyncio


async def buscar(nome: str, pausa: float) -> str:
    print(f"{nome}: iniciada")
    await asyncio.sleep(pausa)
    print(f"{nome}: concluída")
    return f"resultado de {nome}"


async def main() -> None:
    tarefa_a = asyncio.create_task(buscar("A", 0.3))
    tarefa_b = asyncio.create_task(buscar("B", 0.1))

    resultado_a = await tarefa_a
    resultado_b = await tarefa_b
    print(resultado_a)
    print(resultado_b)


asyncio.run(main())

Dica

Teste localmente

Salve o código em um arquivo .py e execute-o. Os valores de pausa servem apenas para tornar o exemplo visível: não use a ordem nem a duração dos registros como uma garantia do escalonador.

Observar em uma ordem não serializa

Duas ordens diferentes

No exemplo, await tarefa_a observa primeiro o desfecho de A. Mas B já foi agendada e pode concluir antes de A enquanto main está suspensa.

Assim, a ordem dos awaits determina a ordem em que este código observador recebe os resultados. Ela não determina a ordem em que Tasks já agendadas concluem.

Exemplo

Leitura possível dos registros

Uma execução pode mostrar:

A: iniciada → B: iniciada → B: concluída → A: concluída → resultado de A → resultado de B

Mesmo com B concluindo antes, main só imprime o resultado de B depois de receber o de A, porque aguarda tarefa_a primeiro.

Monte a sequência concorrente

Ordem do código

Organize os blocos para agendar A e B antes de observar seus resultados.

  1. resultado_a = await tarefa_a
  2. resultado_b = await tarefa_b
  3. tarefa_b = asyncio.create_task(buscar("B", 0.1))
  4. tarefa_a = asyncio.create_task(buscar("A", 0.3))

Explique a diferença

Por que tarefa_b pode concluir antes de tarefa_a mesmo quando o código usa await tarefa_a antes de await tarefa_b?

Escreva pelo menos 40 caracteres (0/40).

Passo 3 de 8

Manter referências e observar os desfechos

Guarde as tarefas que você cria e observe cada resultado ou falha antes de encerrar a corrotina coordenadora.

Referência não é observação

Responsabilidade por uma Task

Depois de criar uma Task, mantenha uma referência forte a ela — em uma variável ou em uma coleção — enquanto seu desfecho ainda importa. O laço de eventos mantém referências fracas às Tasks, então não trate uma Task sem referência própria como trabalho acompanhado.

Mas guardar a referência não basta: você precisa observar o desfecho. Ao usar await tarefa, recebe o valor retornado ou a exceção produzida pela corrotina.

Duas responsabilidades distintas

Uma coleção mantém as Tasks acessíveis; os awaits recuperam o desfecho de cada uma.

Diagrama mostrando uma lista com três referências fortes para Tasks; duas chegam a resultados e uma chega a uma exceção, todas observadas por awaits na corrotina coordenadora.

Guardar permite acompanhar; aguardar revela sucesso ou falha.

Atenção

Falha não observada

Uma mensagem como “Task exception was never retrieved” é um indício de que uma Task falhou e ninguém recuperou sua exceção. Não dependa do instante em que esse aviso aparece: organize o código para observar explicitamente todos os desfechos relevantes.

Observar todas, mesmo com uma falha

Uma Task não encerra as outras

Tasks criadas separadamente não são canceladas automaticamente porque uma delas falhou. Se uma falha é esperada e você quer continuar acompanhando as demais, trate essa falha no ponto em que aguarda aquela Task e prossiga para as outras.

Referências em uma lista e tratamento localizado

Execute este script localmente. A ordem das mensagens pode variar, mas os três desfechos devem ser observados.

python
import asyncio


async def operacao(nome: str) -> str:
    await asyncio.sleep(0.1)
    if nome == "validar":
        raise ValueError("dados inválidos")
    return f"resultado de {nome}"


async def main() -> None:
    tarefas = [
        asyncio.create_task(operacao("baixar")),
        asyncio.create_task(operacao("validar")),
        asyncio.create_task(operacao("salvar")),
    ]

    for tarefa in tarefas:
        try:
            resultado = await tarefa
        except ValueError as erro:
            print(f"falha observada: {erro}")
        else:
            print(f"sucesso observado: {resultado}")


asyncio.run(main())

Dica

Leitura do fluxo

A lista preserva as três referências. O laço não interrompe as observações ao encontrar ValueError: ele registra a falha daquela Task e continua aguardando as restantes. Não há promessa de ordem global de execução; aqui, a ordem de observação é a ordem da lista.

Diagnostique o abandono

Escolha a organização responsável

Três Tasks já foram criadas e guardadas em tarefas. A primeira pode lançar ValueError, mas as outras duas ainda precisam ter seus desfechos observados. Qual organização atende a esse objetivo?

Passo 4 de 8

Entender o pedido de cancelamento

Entenda por que cancelar uma Task é um pedido cooperativo e quando esse pedido pode ser processado.

Cancelar não é interromper imediatamente

Um pedido, não uma confirmação

task.cancel() solicita que a Task seja cancelada. A chamada não confirma que ela já parou, nem que seu desfecho já é conhecido.

No asyncio, o cancelamento é cooperativo: a corrotina precisa voltar ao controle do laço de eventos para receber esse pedido. Até lá, a Task pode continuar pendente ou até terminar por outro motivo.

Do pedido ao desfecho

A solicitação e o cancelamento efetivo são estados diferentes.

Diagrama temporal mostrando uma Task em execução, um pedido de cancelamento, uma suspensão em await, a entrega de CancelledError e o estado final cancelado.

O pedido feito por cancel() só pode ser efetivado quando a corrotina tem uma oportunidade de retomar sob controle do laço.

Exemplo

Solicitação separada do fim

tarefa.cancel()
print(tarefa.done())

Logo após cancel(), done() pode ainda ser False. Isso significa apenas que o desfecho da Task ainda não foi concluído e observado.

Quando a corrotina recebe o pedido

Entrega cooperativa

Quando a Task volta a uma oportunidade de suspensão ou retomada controlada pelo laço — por exemplo, em torno de um await — o asyncio pode entregar asyncio.CancelledError à corrotina.

Assim, uma Task que já começou pode receber um pedido de cancelamento. Isso contrasta com um trabalho que já está em execução em um executor, que não pode ser interrompido dessa forma por Future.cancel().

Atenção

Sem interrupção preemptiva

cancel() não interrompe à força código síncrono bloqueante nem um cálculo longo que não oferece suspensão. Enquanto esse código ocupa a thread do laço, o laço não avança para entregar o pedido de cancelamento.

Evite concluir que uma chamada a cancel() torna imediatamente responsiva uma Task que bloqueia o laço.

Verifique a previsão

Pedido versus conclusão

Depois de chamar task.cancel(), é garantido que a Task já terminou cancelada quando a chamada retorna.

Limite cooperativo

Uma Task que executa processamento longo sem await pode atrasar a efetivação de um pedido de cancelamento.

Passo 5 de 8

Cancelar, aguardar e confirmar

Solicite o cancelamento de uma tarefa, aguarde seu desfecho e diferencie término comum de cancelamento efetivado.

Do pedido à confirmação

Não abandone a tarefa

task.cancel() faz um pedido cooperativo; ele não confirma que a tarefa já terminou. Depois do pedido, mantenha a referência e faça await task para observar o desfecho.

Quando o cancelamento é efetivado, esse await propaga asyncio.CancelledError para quem está observando a tarefa. Nesse cenário, o observador que pediu o cancelamento pode capturar especificamente essa confirmação.

Fluxo de encerramento explícito

A consulta de estado complementa a espera, mas não a substitui.

Diagrama mostrando uma Task ativa, seguida por um pedido cancel(), um ponto de await, a propagação de CancelledError para o observador e o estado final cancelled.

Peça o cancelamento, aguarde a Task e então interprete seu estado final.

Código: cancelar e observar

Um observador responsável

Execute este script localmente com Python 3.

python
import asyncio


async def operacao() -> None:
    while True:
        print("trabalhando")
        await asyncio.sleep(1)


async def main() -> None:
    task = asyncio.create_task(operacao())

    await asyncio.sleep(0.1)
    task.cancel()

    try:
        await task
    except asyncio.CancelledError:
        print("cancelamento confirmado")

    print(f"done={task.done()}, cancelled={task.cancelled()}")


asyncio.run(main())

Dica

A exceção certa importa

asyncio.CancelledError herda de BaseException. Portanto, except Exception: não a captura. Capture asyncio.CancelledError de forma explícita quando você estiver confirmando o cancelamento que solicitou.

Ela também é diferente de concurrent.futures.CancelledError: importe e capture a classe do mecanismo que produziu a exceção. Aqui, a classe correta é asyncio.CancelledError.

Estado final não é apenas done()

Concluída não significa cancelada

task.done() é True quando a Task chegou a qualquer desfecho: retorno normal, exceção ou cancelamento. Já task.cancelled() só é True se ela realmente terminou cancelada.

Não fique consultando esses métodos em um laço para esperar. await task é a sincronização que permite observar o desfecho. Mesmo que a tarefa já tenha acabado antes de cancel(), ainda faça await task: ela pode ter retornado um valor ou produzido outra exceção.

Complete o fluxo

Após task.cancel(), use await ____ para observar o desfecho da tarefa.

Verifique seu raciocínio

Qual estado confirma o cancelamento?

Depois de aguardar uma Task, qual condição indica especificamente que ela terminou por cancelamento efetivado?

Explique a escolha

Por que except Exception não confirma o cancelamento e por que task.done() sozinho não basta para saber que uma tarefa foi cancelada?

Escreva pelo menos 40 caracteres (0/40).

Passo 6 de 8

Limpar recursos e preservar o cancelamento

Use limpeza garantida na corrotina e deixe o cancelamento continuar até quem solicitou a parada.

Limpeza ocorre na saída do bloco protegido

Recurso adquirido, recurso liberado

Quando uma corrotina adquire um recurso, coloque o uso desse recurso dentro de um try e a liberação no finally. O finally é executado tanto na conclusão normal quanto quando asyncio.CancelledError sai do bloco.

A regra é: adquira o recurso antes do try e entre imediatamente no bloco protegido. Assim, depois que usar_recurso() começou, liberar_recurso() será chamado antes de a corrotina terminar.

Fluxo do cancelamento com limpeza

O pedido de cancelamento não elimina a etapa de limpeza: ele faz a corrotina sair pelo finally, que libera o recurso, e só então o cancelamento chega ao observador.

Diagrama mostrando uma corrotina que adquire um recurso, usa o recurso, recebe um pedido de cancelamento, executa a liberação no finally e propaga o cancelamento ao chamador.

O finally fica entre a saída do trabalho e a confirmação do cancelamento ao chamador.

Padrão mínimo com finally

O raise sem argumentos relança a mesma exceção de cancelamento após qualquer ação específica.

python
import asyncio

async def usar_recurso() -> None:
    recurso = "conexão simulada"
    print(f"adquiri: {recurso}")
    try:
        print("usando recurso")
        await asyncio.sleep(10)
    finally:
        print(f"liberei: {recurso}")

async def principal() -> None:
    tarefa = asyncio.create_task(usar_recurso())
    await asyncio.sleep(0)
    tarefa.cancel()

    try:
        await tarefa
    except asyncio.CancelledError:
        print("cancelamento confirmado no chamador")

asyncio.run(principal())

Não transforme cancelamento em sucesso

A confirmação pertence ao chamador

A tarefa que recebeu o cancelamento deve limpar o que lhe pertence e permitir que CancelledError continue. Já o chamador que pediu o cancelamento pode capturar especificamente essa exceção ao fazer await tarefa, pois ali ele está confirmando o desfecho esperado.

Se a própria tarefa captura CancelledError e faz return, ela aparenta ter terminado com sucesso. O mesmo risco existe com um return dentro de finally: ele substitui a exceção que estava sendo propagada.

Atenção

Dois padrões que suprimem o cancelamento

Evite estes padrões:

except asyncio.CancelledError:
    liberar_recurso()
    return  # transforma cancelamento em retorno normal
finally:
    liberar_recurso()
    return  # também impede a propagação de CancelledError

Se precisar de uma ação exclusiva para cancelamento, faça-a e use raise sem argumentos.

Ação específica, seguida de relançamento

Este formato é útil quando o cancelamento exige um registro ou uma ação adicional antes da limpeza geral.

python
async def usar_recurso() -> None:
    recurso = "conexão simulada"
    print(f"adquiri: {recurso}")
    try:
        await asyncio.sleep(10)
    except asyncio.CancelledError:
        print("cancelamento recebido; preparando encerramento")
        raise
    finally:
        print(f"liberei: {recurso}")

Prática local: cancelar sem abandonar a tarefa

Execute e observe as evidências

No seu computador, salve o script abaixo como cancelar_recurso.py e execute python cancelar_recurso.py. Ele usa apenas a biblioteca padrão e um recurso simulado.

A ordem exata de todos os registros não é o foco. Verifique estas evidências: o recurso é adquirido, começa a ser usado, é liberado após o pedido de cancelamento e o chamador confirma CancelledError.

Script completo

A pausa curta em principal() apenas dá uma oportunidade para a tarefa começar antes do pedido; ela não é uma garantia de duração.

python
import asyncio

async def processar_arquivo() -> None:
    arquivo = "relatorio.csv (simulado)"
    print(f"abri: {arquivo}")
    try:
        print("processamento iniciado")
        await asyncio.sleep(10)
        print("processamento concluído")
    except asyncio.CancelledError:
        print("cancelamento recebido no processamento")
        raise
    finally:
        print(f"fechei: {arquivo}")

async def principal() -> None:
    tarefa = asyncio.create_task(processar_arquivo())
    await asyncio.sleep(0)

    print("solicitando cancelamento")
    tarefa.cancel()

    try:
        await tarefa
    except asyncio.CancelledError:
        print("cancelamento confirmado no chamador")

asyncio.run(principal())

Verifique o raciocínio

Evidências da execução

Depois de executar o script, quais registros mostram que o recurso foi liberado e que o cancelamento continuou até o chamador? Explique em uma ou duas frases.

Escreva pelo menos 80 caracteres (0/80).

Limite do finally

Por que adquirir um recurso muito antes de entrar no try reduz a garantia de limpeza do finally?

Escreva pelo menos 80 caracteres (0/80).

Passo 7 de 8

Acompanhar a propagação entre tarefas

Identifique como uma dependência ativa de await pode encaminhar um pedido de cancelamento e onde cada corrotina deve preservar sua limpeza.

Dependência ativa muda o caminho

Criar não é o mesmo que aguardar

create_task() cria uma tarefa independente, mas não cria por si só uma regra de cancelamento entre quem criou e quem foi criada. A relação relevante surge quando uma tarefa fica suspensa em await outra_task.

Se a tarefa aguardante receber cancelamento enquanto espera, o pedido pode ser encaminhado à tarefa aguardada. Assim, ambas precisam estar preparadas para sair: a aguardada limpa seus próprios recursos e propaga CancelledError; a aguardante também executa sua própria limpeza e continua propagando o cancelamento.

Criação versus dependência de espera

Compare as setas: criar uma tarefa registra responsabilidade de acompanhamento; aguardar uma tarefa cria uma dependência ativa durante aquele await.

Diagrama com uma tarefa coordenadora e uma tarefa trabalhadora. À esquerda, uma seta pontilhada de criação sem caminho de cancelamento automático. À direita, uma seta contínua de await, com pedido de cancelamento descendo da coordenadora para a trabalhadora e caminhos de limpeza em ambas.

Somente a espera ativa com await pode encaminhar o cancelamento à tarefa aguardada.

Dica

Responsabilidades diferentes

Mesmo quando o cancelamento é encaminhado, mantenha referências e observe os desfechos das tarefas que você criou. Propagação de cancelamento não substitui a responsabilidade de acompanhar uma tarefa.

Limpar nos dois níveis e propagar

Uma coordenadora aguardando uma trabalhadora

Execute este script localmente. Ele cancela a coordenadora enquanto ela está em await trabalhadora; observe que os dois blocos finally são alcançados.

python
import asyncio


async def trabalhadora() -> None:
    print("trabalhadora: recurso adquirido")
    try:
        await asyncio.sleep(10)
    finally:
        print("trabalhadora: recurso liberado")


async def coordenadora() -> None:
    tarefa_trabalhadora = asyncio.create_task(trabalhadora())
    try:
        print("coordenadora: aguardando trabalhadora")
        await tarefa_trabalhadora
    finally:
        print("coordenadora: limpeza concluída")


async def principal() -> None:
    tarefa_coordenadora = asyncio.create_task(coordenadora())
    await asyncio.sleep(0)  # dá oportunidade para a coordenadora iniciar

    tarefa_coordenadora.cancel()
    try:
        await tarefa_coordenadora
    except asyncio.CancelledError:
        print("principal: cancelamento da coordenadora confirmado")


asyncio.run(principal())

Leia o fluxo do exemplo

principal() confirma o cancelamento que ela solicitou à coordenadora. Dentro de coordenadora(), porém, não há um except asyncio.CancelledError que termine normalmente: o finally limpa e a exceção continua seu caminho.

A trabalhadora também não suprime o cancelamento. Sua limpeza acontece no finally, e CancelledError volta para a coordenadora. A ordem exata das mensagens pode variar, mas as duas limpezas devem ocorrer antes da confirmação em principal().

Atenção

Não descarte o cancelamento da coordenadora

Se a própria coordenadora recebe CancelledError, capturá-lo e fazer return normalmente transforma um cancelamento em aparente sucesso. Capture-o dentro da coordenadora apenas se precisar de uma ação específica e, depois, use raise sem argumentos para preservá-lo.

Mapeie as relações

Associe a situação ao efeito

Relacione cada situação ao efeito mais adequado.

Toque em um item e depois no par correspondente.

Resumo

Regra para prever o fluxo

Antes de cancelar, desenhe quem criou quem e quem está aguardando quem no momento.

  • create_task() cria uma tarefa, mas não forma uma árvore automática de cancelamento.
  • Uma tarefa suspensa em await outra_task pode encaminhar à outra um cancelamento que recebeu.
  • A tarefa aguardada e a aguardante precisam limpar seus próprios recursos.
  • A coordenadora que recebeu cancelamento não deve confundir essa exceção com uma confirmação que possa descartar.

Passo 8 de 8

Aplicação final: encerrar sem abandonar tarefas

Integre agendamento concorrente, observação de resultado e falha, cancelamento e limpeza em um script local completo.

O que o programa precisa comprovar

Quatro evidências, sem ordem rígida

Neste fechamento, você vai executar um script com três Tasks independentes: uma retorna um resultado, uma falha de modo esperado e outra é cancelada durante o uso de um recurso simulado.

O objetivo não é obter uma ordem exata de mensagens. Como as tarefas progridem concorrentemente, a ordem pode variar. Confirme, porém, estas evidências:

  • um resultado foi observado;
  • a falha esperada foi observada;
  • o recurso da tarefa cancelada foi liberado;
  • o cancelamento foi confirmado após aguardar a Task.

Mesmo depois de observar a falha de uma Task, a função coordenadora deve continuar responsável pelas outras referências que criou.

Responsabilidades da coordenadora

Cada seta representa uma responsabilidade explícita: criar e guardar, observar o desfecho e, para a tarefa restante, cancelar e aguardar.

Diagrama com uma função coordenadora ligada a três tarefas: resultado observado, falha observada e tarefa cancelada com recurso liberado antes da confirmação.

A falha de uma Task criada separadamente não elimina a obrigação de acompanhar as demais.

Script completo para executar localmente

Crie e execute

No seu computador, crie um arquivo chamado encerrar_tarefas.py, copie o código completo abaixo e execute python encerrar_tarefas.py. Ele usa somente a biblioteca padrão.

encerrar_tarefas.py

Três Tasks são agendadas antes de qualquer espera. A coordenadora observa cada desfecho e encerra explicitamente a tarefa longa.

python
import asyncio


async def buscar_resumo() -> str:
    await asyncio.sleep(0.05)
    return "resumo pronto"


async def carregar_configuracao() -> str:
    await asyncio.sleep(0.02)
    raise ValueError("configuração inválida")


async def acompanhar_eventos() -> None:
    recurso_aberto = False
    try:
        recurso_aberto = True
        print("eventos: recurso adquirido")
        while True:
            await asyncio.sleep(1)
    finally:
        if recurso_aberto:
            print("eventos: recurso liberado")


async def main() -> None:
    resumo_task = asyncio.create_task(buscar_resumo())
    config_task = asyncio.create_task(carregar_configuracao())
    eventos_task = asyncio.create_task(acompanhar_eventos())

    try:
        resumo = await resumo_task
        print(f"resultado observado: {resumo}")
    except Exception as erro:
        print(f"falha inesperada no resumo: {erro}")

    try:
        await config_task
    except ValueError as erro:
        print(f"falha observada: {erro}")

    eventos_task.cancel()
    try:
        await eventos_task
    except asyncio.CancelledError:
        print("cancelamento confirmado")


asyncio.run(main())

Dica

O detalhe que encerra corretamente

cancel() apenas solicita o cancelamento. O await eventos_task dá oportunidade para a corrotina receber CancelledError, executar o finally e propagar o cancelamento; o observador então o confirma com uma captura específica.

Verifique os desfechos observáveis

Leia a saída por evidências

Você deve encontrar mensagens equivalentes a estas, embora a posição de eventos: recurso adquirido possa variar:

eventos: recurso adquirido
resultado observado: resumo pronto
falha observada: configuração inválida
eventos: recurso liberado
cancelamento confirmado

O finally em acompanhar_eventos() libera o recurso antes de await eventos_task terminar com CancelledError. A tarefa não transforma o cancelamento em retorno normal: ela deixa a exceção continuar até quem solicitou o cancelamento.

Relate sua execução

Depois de executar o script, explique como você observou: o resultado, a falha esperada, a limpeza e o cancelamento. Por que apenas chamar eventos_task.cancel() seria insuficiente?

Escreva pelo menos 180 caracteres (0/180).

Síntese: responsabilidade por cada Task

Resumo

Checklist de encerramento explícito

Use esta sequência sempre que criar Tasks separadamente e precisar coordená-las manualmente.

  • Agende as corrotinas com asyncio.create_task() e mantenha referências enquanto seus desfechos importarem.
  • Observe cada resultado ou exceção com await; uma falha esperada em uma Task não acompanha automaticamente as outras.
  • Para encerrar uma Task, solicite com cancel() e depois faça await nela.
  • Na corrotina cancelada, use finally para liberar recursos adquiridos no bloco protegido.
  • Não suprima asyncio.CancelledError dentro da tarefa sem uma decisão explícita; após limpeza, ele deve continuar se propagando.
  • Confirme o cancelamento no observador apropriado, depois que a Task terminar sua limpeza.

Tutorial concluído

Parabéns! Você concluiu: Gerenciar tarefas e cancelamento no asyncio

Parabéns! Você já consegue criar Tasks concorrentes sem abandoná-las: guarda referências, observa resultados e falhas, solicita cancelamento, aguarda a limpeza e preserva CancelledError. No próximo tutorial, você verá como estruturar essa coordenação com asyncio.TaskGroup.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso