
Passo 1 de 8
Vincular tarefas a um escopo responsável
Entenda como o TaskGroup vincula tarefas relacionadas a um escopo que responde pelo encerramento delas.
Trilha de aprendizado · Nível 13 · Tutorial 8
Ao concluir, você será capaz de manter tarefas relacionadas em um escopo comum que aguarda sua finalização, coordena cancelamentos diante de falhas e apresenta os erros sem abandonar tarefas.
Vincular tarefas a um escopo responsável
Entenda como o TaskGroup vincula tarefas relacionadas a um escopo que responde pelo encerramento delas. 2 min
Abrir o TaskGroup e registrar tarefas
Abra um escopo assíncrono, registre operações relacionadas nele e mantenha as referências às tarefas criadas. 3 min
Recuperar resultados após o encerramento
Use as referências às tarefas para consolidar resultados somente depois que o TaskGroup concluir sua saída assíncrona. 3 min
Entender o encerramento provocado por falha
Veja como o TaskGroup coordena o cancelamento e a limpeza das tarefas irmãs quando uma delas falha. 3 min
Distinguir cancelamento isolado de falha
Compare cancelamento, retorno e falha para consultar resultados de tarefas com segurança. 2 min
Interpretar as falhas agrupadas
Leia os erros apresentados por um TaskGroup como um conjunto de falhas efetivamente observadas durante seu encerramento. 3 min
Tratar somente os subgrupos esperados
Selecione falhas esperadas dentro de um ExceptionGroup sem esconder as demais. 4 min
Aplicar e revisar a coordenação completa
Pratique os quatro cenários centrais do TaskGroup em um script local e revise as garantias de coordenação, cancelamento e propagação de falhas. 5 min

Passo 1 de 8
Entenda como o TaskGroup vincula tarefas relacionadas a um escopo que responde pelo encerramento delas.
Ao criar tarefas relacionadas, não basta iniciá-las: alguém precisa acompanhar o encerramento delas. Na concorrência estruturada, esse responsável é um escopo.
Um TaskGroup reúne tarefas que pertencem à mesma operação. Enquanto esse escopo não termina, o grupo acompanha as tarefas que registrou. Assim, a responsabilidade de aguardar a finalização e coordenar o encerramento deixa de ficar espalhada pelo programa.
A saída do escopo depende do encerramento das tarefas registradas.

O grupo delimita quais tarefas fazem parte da mesma operação e só conclui sua saída após cuidar delas.
A responsabilidade do grupo alcança somente as tarefas que ele registra. Uma tarefa criada de outra forma pode estar no mesmo trecho de código e ainda assim ficar fora desse vínculo.
Em particular, chamar asyncio.create_task(...) dentro da região em que existe um grupo não inclui automaticamente essa tarefa no grupo. Ela continua exigindo acompanhamento explícito de quem a criou.
Exemplo
Imagine uma operação que inicia três tarefas:
TaskGroup → o grupo responde pelo encerramento dela.TaskGroup → o grupo responde pelo encerramento dela.asyncio.create_task(...) no mesmo trecho → ela não passa a ser responsabilidade do grupo apenas por proximidade.O próximo passo mostrará a sintaxe para registrar A e B corretamente. Por enquanto, guarde a regra: o responsável é definido pela forma de criação, não pela posição visual no código.
Faça a correspondência correta.
Toque em um item e depois no par correspondente.

Passo 2 de 8
Abra um escopo assíncrono, registre operações relacionadas nele e mantenha as referências às tarefas criadas.
Nesta trilha, a referência é o Python 3.12 ou superior. asyncio.TaskGroup e a sintaxe except* existem desde o Python 3.11.
Um async with é adequado quando a entrada ou a saída de um contexto pode precisar de await. Por isso, ele usa os métodos aguardáveis __aenter__ e __aexit__. No caso de TaskGroup, a saída do contexto participa do encerramento coordenado das tarefas registradas.
O corpo registra as operações no grupo; a saída assíncrona só acontece depois do trabalho coordenado pelo grupo.

async with delimita o escopo responsável pelas tarefas registradas.
Dentro de uma função definida com async def, abra o escopo com async with asyncio.TaskGroup() as grupo.
Passe a corrotina para grupo.create_task(...) e guarde a Task retornada. Registre todas as operações relacionadas antes de sair do bloco. Não faça await em cada criação: isso faria uma operação terminar antes de iniciar a próxima.
Atenção: asyncio.create_task(...) cria uma tarefa no laço de eventos, mas não a registra automaticamente neste TaskGroup.
As variáveis tarefa_a e tarefa_b guardam as referências retornadas pelo grupo.
async with asyncio.TaskGroup() as grupo:
tarefa_a = grupo.create_task(operacao_a())
tarefa_b = grupo.create_task(operacao_b())
# O próximo step mostrará quando consultar essas referências.Crie um arquivo, por exemplo grupo.py, cole o código e execute python grupo.py. As pausas com asyncio.sleep apenas simulam operações de entrada e saída; não representam uma medição de tempo.
Observe que as três mensagens de início aparecem antes das mensagens de término, pois as tarefas foram registradas sem esperas individuais.
Todas as operações usam somente a biblioteca padrão.
import asyncio
async def consultar(nome: str, demora: float) -> str:
print(f"Início: {nome}")
await asyncio.sleep(demora)
print(f"Fim: {nome}")
return f"dados de {nome}"
async def main() -> None:
async with asyncio.TaskGroup() as grupo:
usuarios = grupo.create_task(consultar("usuários", 0.2))
pedidos = grupo.create_task(consultar("pedidos", 0.1))
estoque = grupo.create_task(consultar("estoque", 0.15))
print("O contexto do grupo foi encerrado.")
# As referências usuarios, pedidos e estoque continuam disponíveis aqui.
# Seus resultados serão tratados no próximo step.
if __name__ == "__main__":
asyncio.run(main())Dica
Mesmo quando ainda não precisa usar as tarefas logo após o bloco, guardar as referências torna explícita a relação entre cada operação iniciada e sua Task.
Para registrar baixar_catalogo() no TaskGroup chamado grupo, complete:
catalogo = grupo._____(baixar_catalogo())
Dentro de async with asyncio.TaskGroup() as grupo:, qual linha registra sincronizar() no grupo?

Passo 3 de 8
Use as referências às tarefas para consolidar resultados somente depois que o TaskGroup concluir sua saída assíncrona.
Depois de registrar as tarefas, o corpo do async with pode chegar ao fim antes de elas terminarem. A saída assíncrona do TaskGroup então aguarda as tarefas registradas. Somente na linha após o bloco é seguro consolidar os resultados neste cenário: todas as tarefas retornam normalmente.
A linha de saída do async with é uma fronteira: o código posterior só é executado após o encerramento bem-sucedido das tarefas do grupo.

O fim visual do corpo não é o fim do grupo: a saída do contexto aguarda as tarefas.
Dica
Task.result() recupera um resultado já concluído; ela não espera a tarefa terminar. Se for chamada enquanto a tarefa ainda está pendente, pode levantar InvalidStateError. Deixe a consolidação para depois do async with.
TaskGroup coordena o ciclo de vida das tarefas, mas não devolve automaticamente uma lista de resultados. Guarde cada Task retornada por grupo.create_task() e associe-a à entrada correspondente. Assim, você pode apresentar os valores na ordem das entradas, mesmo que as tarefas terminem em outra ordem.
Salve como resultados_taskgroup.py e execute com Python 3.11 ou superior: python resultados_taskgroup.py.
import asyncio
async def consultar(produto: str, demora: float) -> str:
await asyncio.sleep(demora)
return f"{produto}: disponível"
async def main() -> None:
pedidos = [
("caderno", 0.20),
("caneta", 0.05),
("mochila", 0.10),
]
tarefas: dict[str, asyncio.Task[str]] = {}
async with asyncio.TaskGroup() as grupo:
for produto, demora in pedidos:
tarefas[produto] = grupo.create_task(consultar(produto, demora))
print("Tarefas registradas; saindo do corpo do bloco.")
print("O TaskGroup encerrou com sucesso.")
resultados = [tarefas[produto].result() for produto, _ in pedidos]
print("Resultados na ordem dos pedidos:")
for resultado in resultados:
print("-", resultado)
if __name__ == "__main__":
asyncio.run(main())Após executar o script, indique: em que ponto os resultados são consultados, por que eles já estão disponíveis nesse ponto e por que a ordem impressa não prova uma ordem obrigatória de conclusão das tarefas.
Escreva pelo menos 80 caracteres (0/80).

Passo 4 de 8
Veja como o TaskGroup coordena o cancelamento e a limpeza das tarefas irmãs quando uma delas falha.
Se uma tarefa registrada no TaskGroup levanta uma exceção diferente de asyncio.CancelledError, o grupo inicia o encerramento: solicita o cancelamento das tarefas irmãs que ainda não terminaram.
Isso não é uma interrupção instantânea. O cancelamento é cooperativo: cada tarefa recebe CancelledError quando volta a um ponto em que pode responder a ele, normalmente em um await. Antes de apresentar a falha ao código externo, o grupo espera essas tarefas encerrarem.
A ordem dos marcos é coordenada; a ordem em que tarefas irmãs imprimem mensagens não é garantida.

Falha comum → pedido de cancelamento às irmãs pendentes → limpeza e término → apresentação do erro.
Execute este script no seu computador. As mensagens de limpeza das tarefas A e B podem aparecer em ordens diferentes.
import asyncio
async def acompanhar(nome: str) -> None:
try:
print(f"{nome}: iniciada")
await asyncio.sleep(10)
print(f"{nome}: concluída")
except asyncio.CancelledError:
print(f"{nome}: recebendo cancelamento")
raise
finally:
print(f"{nome}: limpeza concluída")
async def falhar() -> None:
await asyncio.sleep(0.1)
raise RuntimeError("fonte indisponível")
async def principal() -> None:
async with asyncio.TaskGroup() as grupo:
grupo.create_task(acompanhar("A"))
grupo.create_task(acompanhar("B"))
grupo.create_task(falhar())
await asyncio.sleep(5)
print("Esta linha não é uma garantia")
asyncio.run(principal())Atenção
O finally executa a limpeza tanto no retorno normal quanto no cancelamento. Se você capturar asyncio.CancelledError para registrar algo, relance-o com raise. Engolir esse sinal pode atrasar ou comprometer a coordenação do encerramento.
falhar() produz a falha comum. O grupo então pede cancelamento a A e B, aguarda a execução do finally de ambas e só depois deixa RuntimeError sair de asyncio.run.
O await asyncio.sleep(5) no corpo do async with pode ser interrompido pela falha; portanto, a última linha do bloco não é garantida. Ainda assim, o grupo coordena o encerramento das tarefas que registrou.
Considere que uma tarefa do grupo levanta RuntimeError enquanto duas irmãs ainda aguardam. Coloque os eventos na sequência causal correta.
Por que o TaskGroup espera as tarefas irmãs terminarem antes de apresentar a falha que iniciou o encerramento? Inclua o papel da limpeza e explique por que não se deve depender da ordem das mensagens das irmãs.
Escreva pelo menos 80 caracteres (0/80).

Passo 5 de 8
Compare cancelamento, retorno e falha para consultar resultados de tarefas com segurança.
Em um TaskGroup, uma tarefa filha que termina com asyncio.CancelledError pode estar cancelada enquanto as demais continuam e retornam normalmente. Esse cancelamento isolado, por si só, não aciona a política aplicada quando uma tarefa falha com uma exceção comum, como ValueError.
Assim, o async with pode terminar sem levantar erro, mas isso não significa que toda Task tenha um valor de retorno.
Compare o efeito de cada término sobre as tarefas irmãs e sobre a saída do grupo.

Retorno e cancelamento isolado podem permitir a saída normal; uma exceção comum em uma filha inicia o encerramento coordenado das irmãs.
Execute este script localmente com Python 3.11 ou superior. A tarefa lenta é cancelada, mas rapida ainda conclui.
import asyncio
async def trabalho(nome: str, demora: float) -> str:
try:
await asyncio.sleep(demora)
return f"{nome}: concluído"
finally:
print(f"limpeza de {nome}")
async def main() -> None:
async with asyncio.TaskGroup() as grupo:
rapida = grupo.create_task(trabalho("rápida", 0.01))
lenta = grupo.create_task(trabalho("lenta", 1.0))
await asyncio.sleep(0)
lenta.cancel()
# O grupo já aguardou o encerramento de ambas as tarefas.
for tarefa in (rapida, lenta):
if tarefa.cancelled():
print("tarefa cancelada: sem resultado")
else:
print(tarefa.result())
asyncio.run(main())Dica
Depois da saída do grupo, quando cancelamentos forem possíveis, consulte tarefa.cancelled() antes de chamar tarefa.result(). Para uma tarefa cancelada, result() levanta asyncio.CancelledError; cancelamento não é um resultado de sucesso.
Relacione cada desfecho ao comportamento correspondente no TaskGroup.
Toque em um item e depois no par correspondente.
Se um TaskGroup termina sem levantar erro, então toda tarefa criada nele possui um valor que pode ser obtido com result().

Passo 6 de 8
Leia os erros apresentados por um TaskGroup como um conjunto de falhas efetivamente observadas durante seu encerramento.
Quando uma tarefa do TaskGroup falha com uma exceção comum, o grupo inicia o encerramento coordenado. Ao terminar esse processo, ele apresenta as falhas comuns que foram realmente observadas em um ExceptionGroup.
O nome é literal: trata-se de um grupo estruturado de exceções. Mesmo que só uma falha comum tenha sido observada, o TaskGroup a apresenta agrupada. Portanto, não suponha que receberá diretamente um único ValueError, por exemplo.
Leia o grupo como uma árvore: o nó externo representa a apresentação feita pelo TaskGroup; as folhas representam falhas comuns observadas.

CancelledError de uma tarefa cancelada não entra como folha de falha comum no ExceptionGroup.
Exemplo
Imagine este registro possível:
processar: iniciou
salvar: iniciou
processar: ValueError
salvar: recebeu cancelamento; iniciou limpeza
salvar: RuntimeError durante a limpezaA falha de processar faz o grupo solicitar o cancelamento de salvar. Mas salvar ainda executa sua limpeza e pode falhar nela. Ao fim, o grupo pode apresentar um ExceptionGroup com as duas folhas: ValueError e RuntimeError.
As mensagens e a ordem podem variar. O ponto importante é que ambas ocorreram antes de o encerramento terminar.
O grupo inclui falhas que aconteceram de fato. Ele não adivinha erros que uma tarefa cancelada poderia produzir se tivesse continuado a execução.
Assim, se uma tarefa recebeu cancelamento antes de alcançar um futuro raise KeyError, esse KeyError hipotético não aparece. E, se ela termina apenas com CancelledError, isso não é incluído como falha comum na árvore.
Dica
Não programe assumindo uma quantidade fixa de folhas nem a ordem entre erros de tarefas irmãs. A intercalação entre tarefas e o momento em que respondem ao cancelamento podem mudar o conjunto observado.
Considere este registro:
buscar: iniciou
registrar: iniciou
buscar: ValueError
registrar: recebeu cancelamento
registrar: RuntimeError na limpezaQual representação é compatível com as falhas apresentadas pelo TaskGroup?
Uma tarefa validar produz ValueError. Isso faz o grupo cancelar exportar. Durante a limpeza, exportar produz OSError; ela também teria produzido KeyError mais tarde, mas não chegou a esse ponto.
Quais falhas podem aparecer no ExceptionGroup e por que CancelledError e KeyError não devem ser acrescentados?
Escreva pelo menos 80 caracteres (0/80).
Resumo
TaskGroup apresenta falhas comuns observadas em um ExceptionGroup, inclusive quando há apenas uma folha.CancelledError não é uma falha comum adicionada à árvore.
Passo 7 de 8
Selecione falhas esperadas dentro de um ExceptionGroup sem esconder as demais.
Um TaskGroup apresenta suas falhas ao sair do async with. Por isso, envolva o bloco inteiro com try: os tratadores recebem o ExceptionGroup produzido no encerramento.
Use except* Tipo para selecionar as exceções desse tipo nas folhas do grupo. A variável após as é um subgrupo, mesmo que contenha apenas uma exceção correspondente.
Cada cláusula except* extrai somente a parte compatível. O que não for selecionado continua se propagando.

except* trata um subgrupo por tipo; os ramos não tratados não desaparecem.
Execute com Python 3.11 ou superior. A falha de validação é registrada; a falha de chave continua sendo apresentada depois.
import asyncio
async def validar() -> None:
await asyncio.sleep(0)
raise ValueError("formato de pedido inválido")
async def buscar_configuracao() -> None:
await asyncio.sleep(0)
raise KeyError("REGIAO")
async def executar() -> None:
try:
async with asyncio.TaskGroup() as grupo:
grupo.create_task(validar())
grupo.create_task(buscar_configuracao())
except* ValueError as erros_de_validacao:
print("Falha esperada:", erros_de_validacao)
asyncio.run(executar())Atenção
Não substitua o tratamento seletivo por except* Exception apenas para evitar a propagação: isso também esconderia defeitos que você não decidiu tratar. No exemplo, o subgrupo com KeyError permanece não tratado e é propagado.
No mesmo try, Python não permite misturar except e except*. Se você usa except*, todos os tratadores daquele try devem usar except*.
Complete o tratador:
try:
async with asyncio.TaskGroup() as grupo:
grupo.create_task(validar())
except___ ValueError as erros:
registrar(erros)Quando uma tarefa falha, o grupo já solicitou o cancelamento das irmãs que ainda estavam ativas e aguardou o encerramento delas. Tratar um subgrupo com except* acontece depois disso: não reinicia tarefas canceladas nem lhes atribui um resultado.
Só consolide task.result() no caminho em que o async with terminou com sucesso. Se houve falhas tratadas ou cancelamentos possíveis, defina explicitamente outra política para esses estados.
Um TaskGroup observa uma ValueError e uma KeyError. O código tem somente:
except* ValueError as erros:
registrar(erros)Explique o que essa cláusula recebe, o que acontece com a KeyError e por que ela não retoma as tarefas que foram canceladas.
Escreva pelo menos 80 caracteres (0/80).

Passo 8 de 8
Pratique os quatro cenários centrais do TaskGroup em um script local e revise as garantias de coordenação, cancelamento e propagação de falhas.
Em todos os cenários, as tarefas são registradas no mesmo TaskGroup. O que muda é o desfecho: retorno normal, cancelamento isolado, falha esperada ou uma falha inesperada adicional. A saída do async with continua sendo o ponto que coordena o encerramento das tarefas registradas.
Observe quais tarefas podem fornecer resultado e quando a falha precisa continuar se propagando.

Resultado só é consultado no caminho sem falhas do grupo; uma tarefa cancelada também não possui resultado.
Dica
As mensagens de tarefas irmãs podem aparecer em ordens diferentes. Verifique propriedades: todas as tarefas registradas encerraram; a limpeza ocorreu; resultados foram lidos apenas quando válidos; e a parte inesperada não foi ocultada.
Crie um arquivo chamado taskgroup_revisao.py, copie o script e execute python taskgroup_revisao.py. Altere apenas a constante CENARIO para testar "sucesso", "cancelamento", "esperado" e "misto". Use Python 3.11 ou superior; a referência da trilha é Python 3.12+.
Script autocontido: cada cenário mostra um desfecho coordenado pelo grupo.
import asyncio
CENARIO = "sucesso" # sucesso, cancelamento, esperado ou misto
async def operacao(nome: str, acao: str, eventos: list[str]) -> str:
eventos.append(f"início:{nome}")
try:
if acao == "falhar":
await asyncio.sleep(0.01)
raise ValueError(f"dado inválido em {nome}")
if acao == "cancelar":
await asyncio.sleep(0.01)
raise asyncio.CancelledError()
if acao == "falhar_na_limpeza":
# Esta tarefa será cancelada pela falha da irmã.
await asyncio.sleep(1)
await asyncio.sleep(0.03)
eventos.append(f"retorno:{nome}")
return nome.upper()
finally:
eventos.append(f"limpeza:{nome}")
if acao == "falhar_na_limpeza":
raise KeyError(f"falha inesperada na limpeza de {nome}")
def acoes_do(cenario: str) -> dict[str, str]:
if cenario == "sucesso":
return {"a": "ok", "b": "ok", "c": "ok"}
if cenario == "cancelamento":
return {"a": "ok", "b": "cancelar", "c": "ok"}
if cenario == "esperado":
return {"a": "ok", "b": "falhar", "c": "ok"}
if cenario == "misto":
return {"a": "falhar", "b": "falhar_na_limpeza", "c": "ok"}
raise ValueError("CENARIO inválido")
async def executar(cenario: str) -> None:
eventos: list[str] = []
tarefas: dict[str, asyncio.Task[str]] = {}
try:
async with asyncio.TaskGroup() as grupo:
for nome, acao in acoes_do(cenario).items():
tarefas[nome] = grupo.create_task(operacao(nome, acao, eventos))
except* ValueError as erros_esperados:
print("ValueError tratado:", erros_esperados)
print("Eventos após encerramento:", eventos)
else:
resultados = {
nome: tarefa.result()
for nome, tarefa in tarefas.items()
if not tarefa.cancelled()
}
canceladas = [nome for nome, tarefa in tarefas.items() if tarefa.cancelled()]
print("Resultados válidos:", resultados)
print("Tarefas canceladas:", canceladas)
print("Eventos após encerramento:", eventos)
if __name__ == "__main__":
asyncio.run(executar(CENARIO))sucesso: três resultados e três eventos de limpeza.cancelamento: b aparece entre as canceladas; a e c ainda retornam normalmente.esperado: o ValueError é tratado; as tarefas que ainda estavam pendentes recebem cancelamento, e os eventos de limpeza aparecem.misto: o ValueError é tratado, mas o subgrupo com KeyError permanece sem tratamento e é propagado. Isso é intencional.Atenção
No caminho que entrou em except*, não consolide resultados como se o grupo tivesse terminado com sucesso. E, no caminho normal, teste task.cancelled() antes de chamar task.result(): uma tarefa cancelada não oferece valor de retorno.
Depois de executar ao menos dois cenários, registre: (1) um caso em que resultados podem ser consolidados, (2) o que acontece com as irmãs no cancelamento isolado ou na falha e (3) como você confirmou que uma falha inesperada não foi ocultada.
Escreva pelo menos 180 caracteres (0/180).
Resumo
Use o grupo como o escopo responsável pelas tarefas relacionadas.
grupo.create_task e mantenha as referências retornadas.async with só termina depois de coordenar o encerramento das tarefas registradas.Task.result() apenas após uma saída sem falhas e somente para tarefas não canceladas.except* fora do async with para tratar somente os tipos previstos; subgrupos não tratados continuam se propagando.Parabéns! Você concluiu: Coordenar tarefas com asyncio.TaskGroup
Milhares de cursos online em vídeo, ebooks e áudiobooks.
Para testar seus conhecimentos no decorrer dos cursos online
Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail
Baixe nosso aplicativo pelo QR Code ou pelos links abaixo:.
+ de 10 milhões
de alunos
Certificado grátis e
válido em todo o Brasil
60 mil exercícios
gratuitos
4,8/5 classificação
nas lojas de apps
Cursos gratuitos em
vídeo, ebooks e audiobooks