
Passo 1 de 8
Interpretar o estado de um futuro
Use consultas de estado para observar se um futuro está em execução, finalizado ou efetivamente cancelado, sem confundir finalização com sucesso.
Trilha de aprendizado · Nível 13 · Tutorial 3
Ao concluir, você será capaz de limitar esperas, cancelar trabalho ainda não iniciado e solicitar a parada cooperativa de funções executadas em threads.
Interpretar o estado de um futuro
Use consultas de estado para observar se um futuro está em execução, finalizado ou efetivamente cancelado, sem confundir finalização com sucesso. 2 min
Limitar a espera por um resultado
Use um prazo em Future.result() sem confundir o fim da espera do chamador com o fim do trabalho na thread. 3 min
Separar concluídos e não concluídos com wait
Use wait para observar um lote de futuros sem confundir o fim da espera com o fim de todo o trabalho. 3 min
Cancelar trabalho que ainda não começou
Use o retorno de cancel() para saber se uma tarefa foi efetivamente cancelada e trate futuros cancelados ao recuperar resultados. 3 min
Escolher como encerrar o executor
Defina se o chamador espera, se a fila pendente será cancelada e o que continua acontecendo com tarefas já iniciadas. 3 min
Solicitar parada cooperativa com Event
Use um sinal compartilhado para que trabalhadores em threads encerrem seu próprio trabalho de forma cooperativa. 3 min
Manter o trabalhador capaz de observar a parada
Ajuste pontos de espera para que um trabalhador possa voltar a verificar um pedido cooperativo de parada. 3 min
Aplicar uma política completa de encerramento
Integre espera limitada, cancelamento da fila, parada cooperativa e inspeção final dos futuros em um lote finito. 4 min

Passo 1 de 8
Use consultas de estado para observar se um futuro está em execução, finalizado ou efetivamente cancelado, sem confundir finalização com sucesso.
Um Future representa uma chamada submetida ao executor. Você pode observar seu estado com três métodos:
running() é True somente enquanto a função está sendo executada.done() é True quando o futuro já terminou — com resultado, com exceção ou por cancelamento.cancelled() é True somente quando o cancelamento foi efetivado antes de a função começar.Assim, done() não significa “a função teve sucesso”.
Observe como as consultas se combinam em cada situação.

Um futuro concluído pode ter resultado, exceção ou cancelamento.
Este exemplo usa uma pausa apenas para tornar o estado em execução fácil de observar. As consultas não esperam a conclusão.
from concurrent.futures import ThreadPoolExecutor
import time
def processar() -> str:
time.sleep(1)
return "pronto"
with ThreadPoolExecutor(max_workers=1) as executor:
futuro = executor.submit(processar)
print(futuro.running()) # pode ser True quando a função já iniciou
print(futuro.done()) # False enquanto não finalizou
print(futuro.cancelled()) # False: não houve cancelamento efetivo
resultado = futuro.result()
print(resultado)
print(futuro.done()) # True após a finalização
Dica
Esses métodos são observações momentâneas. Entre futuro.running() e a próxima linha, a função pode terminar. Por isso, não use uma consulta isolada como garantia de uma ação futura; trate o resultado ao recuperá-lo.
Depois de done() retornar True, result() não precisa devolver um valor: se a função falhou, ele propaga a exceção produzida pela função. Um futuro cancelado também está finalizado, mas sua recuperação segue um tratamento próprio, visto mais adiante.
Relacione cada observação à interpretação adequada.
Toque em um item e depois no par correspondente.

Passo 2 de 8
Use um prazo em Future.result() sem confundir o fim da espera do chamador com o fim do trabalho na thread.
Ao chamar futuro.result(timeout=segundos), o prazo limita apenas quanto tempo essa chamada aguardará pelo resultado.
Se o resultado não estiver disponível dentro do prazo, result() lança TimeoutError. Isso não equivale a parar a função submetida: o futuro pode continuar em execução e ficar pronto depois.
O chamador deixa de esperar ao atingir o prazo; a thread trabalhadora segue sua função até ela terminar.

O timeout encerra a espera de result(), não a execução já iniciada da função.
Atenção
Após um TimeoutError, não suponha que a tarefa falhou, terminou ou teve seus efeitos desfeitos. O único fato conhecido é: o resultado não ficou disponível dentro do prazo escolhido para aquela chamada.
Execute este script localmente. A pausa é apenas uma tarefa simulada para tornar a linha do tempo observável.
from concurrent.futures import ThreadPoolExecutor, TimeoutError
from time import sleep
def preparar_relatorio() -> str:
sleep(2)
return "relatório pronto"
with ThreadPoolExecutor(max_workers=1) as executor:
futuro = executor.submit(preparar_relatorio)
try:
print(futuro.result(timeout=0.2))
except TimeoutError:
print("A espera venceu; o trabalhador pode continuar.")
print("Concluído neste instante?", futuro.done())
print("Resultado posterior:", futuro.result())
Exemplo
A primeira chamada a result(timeout=0.2) tende a lançar TimeoutError, porque a função simulada leva mais tempo. Depois, a segunda chamada futuro.result() aguarda o mesmo futuro e obtém "relatório pronto".
O except permite ao chamador decidir o que fazer enquanto o trabalho continua. Nesta etapa, não estamos cancelando nem encerrando o executor de forma especial.
Uma função já está em execução em uma thread. futuro.result(timeout=0.1) lança TimeoutError. Qual afirmação é correta?

Passo 3 de 8
Use wait para observar um lote de futuros sem confundir o fim da espera com o fim de todo o trabalho.
concurrent.futures.wait(futuros, ...) recebe uma coleção de futuros e devolve dois conjuntos: done e not_done.
done: futuros que já terminaram, inclusive os que falharam com exceção ou foram cancelados.not_done: futuros que ainda não terminaram. Eles podem estar aguardando uma thread na fila ou já estar em execução.Essa divisão é uma fotografia do estado no instante em que wait retorna. Conjuntos não preservam a ordem em que você submeteu as tarefas.
O resultado separa finalizados de ainda não finalizados — não separa sucesso de falha.

done inclui resultado, exceção e cancelamento; not_done pode combinar fila e execução.
Por padrão, wait usa return_when=ALL_COMPLETED: só retorna quando todos os futuros terminarem.
Com return_when=FIRST_COMPLETED, retorna assim que pelo menos um futuro termina ou é cancelado. Isso não quer dizer “primeiro resultado bem-sucedido” e também não garante que done terá exatamente um futuro: outros podem ter terminado antes de a chamada devolver o controle.
Você também pode passar timeout. Ao prazo se esgotar, wait simplesmente devolve os conjuntos disponíveis naquele momento. Diferentemente de Future.result(timeout=...), isso não lança TimeoutError e não cancela o trabalho que restou.
O código examina apenas os futuros que já pertencem a done.
from concurrent.futures import FIRST_COMPLETED, wait
# futuros foi criado anteriormente com executor.submit(...)
done, not_done = wait(
futuros,
timeout=2,
return_when=FIRST_COMPLETED,
)
print(f"finalizados agora: {len(done)}")
print(f"ainda não finalizados: {len(not_done)}")
for futuro in done:
try:
print("resultado:", futuro.result())
except Exception as erro:
print("a tarefa terminou com erro:", erro)
# Os futuros em not_done continuam na fila ou em execução.Dica
Chamar futuro.result() para cada elemento de done não cria uma nova espera pela conclusão daquele futuro. Ainda assim, o método pode propagar a exceção produzida pela função; por isso, trate-a ao observar cada resultado.
Complete a constante para que wait retorne quando pelo menos um futuro do lote terminar ou for cancelado:
done, not_done = wait(futuros, return_when=_____)
Você chama wait(futuros, timeout=1) e recebe alguns futuros em done e outros em not_done. Explique o que os elementos de not_done podem estar fazendo e o que o timeout não fez com eles.
Escreva pelo menos 60 caracteres (0/60).

Passo 4 de 8
Use o retorno de cancel() para saber se uma tarefa foi efetivamente cancelada e trate futuros cancelados ao recuperar resultados.
Future.cancel() tenta cancelar um futuro antes de sua função começar a executar. Se o trabalho já estiver em andamento, a chamada não interrompe a thread nem força a função a parar.
Use o valor retornado pela própria chamada como decisão:
True: o futuro foi cancelado — ou já estava cancelado.False: a função está em execução ou o futuro já terminou sem cancelamento.Uma consulta como future.running() é apenas uma observação momentânea. Entre essa consulta e future.cancel(), o executor pode iniciar a tarefa.
O momento em que cancel() é chamado define se a tentativa pode impedir o início da função.

Na fila, cancel() pode retornar True. Depois do início da execução, retorna False e o trabalho segue.
Execute este exemplo localmente. A tarefa primeira ocupa o único trabalhador; por isso, segunda ainda está na fila quando tentamos cancelá-la.
from concurrent.futures import ThreadPoolExecutor, CancelledError
import time
def trabalho(nome: str, demora: float) -> str:
print(f"{nome}: iniciou")
time.sleep(demora)
print(f"{nome}: terminou")
return nome
with ThreadPoolExecutor(max_workers=1) as executor:
primeira = executor.submit(trabalho, "primeira", 0.3)
segunda = executor.submit(trabalho, "segunda", 0.1)
foi_cancelada = segunda.cancel()
print(f"cancel() retornou: {foi_cancelada}")
print(f"done={segunda.done()}, cancelled={segunda.cancelled()}")
try:
print("resultado:", segunda.result())
except CancelledError:
print("segunda foi cancelada; não há resultado para recuperar")
print("primeira:", primeira.result())Um futuro cancelado está finalizado: done() e cancelled() retornam True. Porém, ele não possui um valor de retorno.
Assim, future.result() lança concurrent.futures.CancelledError. Trate essa exceção separadamente quando o cancelamento for um resultado esperado da sua política de coordenação.
Se um futuro aparece em not_done após wait, então uma chamada imediata a cancel() certamente retornará True.
Você chama futuro.running() e recebe False. Logo depois, chama futuro.cancel(), que retorna False. Por que isso é possível? O que seu código deve usar para decidir se precisa tratar CancelledError?
Escreva pelo menos 80 caracteres (0/80).

Passo 5 de 8
Defina se o chamador espera, se a fila pendente será cancelada e o que continua acontecendo com tarefas já iniciadas.
executor.shutdown(...) impede novas submissões. Depois dessa chamada, executor.submit(...) falha com RuntimeError.
A política de encerramento tem duas decisões independentes:
wait: o chamador espera ou retoma o controle agora?cancel_futures: itens que ainda estão na fila devem ser cancelados?Nenhuma dessas opções interrompe uma função que já está em execução.
A configuração afeta de formas diferentes quem chamou o executor, as tarefas na fila e as tarefas já em execução.

wait controla a espera do chamador; cancel_futures alcança apenas a fila. Trabalho ativo continua até a própria função terminar.
| Chamada | Chamador | Trabalho na fila | Trabalho em execução |
| --- | --- | --- | --- |
| shutdown(wait=True, cancel_futures=False) | espera | continua e termina | termina |
| shutdown(wait=False, cancel_futures=False) | retorna logo | continua e termina | termina |
| shutdown(wait=True, cancel_futures=True) | espera | é cancelado se ainda não iniciou | termina |
| shutdown(wait=False, cancel_futures=True) | retorna logo | é cancelado se ainda não iniciou | termina |
cancel_futures vale False por padrão. Mesmo com wait=False, as threads não são eliminadas: o interpretador ainda aguarda o trabalho pendente não cancelado antes de encerrar o programa.
Atenção
shutdown(wait=False) devolve o controle ao seu código sem esperar. Isso não é uma forma de forçar a saída do processo nem de matar threads. Não feche recursos que as funções ainda usam só porque a chamada retornou.
Execute este exemplo para observar a ordem dos eventos.
from concurrent.futures import ThreadPoolExecutor, TimeoutError
from time import sleep
def tarefa_lenta() -> str:
print("trabalhador: começou")
sleep(1)
print("trabalhador: terminou")
return "pronto"
try:
with ThreadPoolExecutor(max_workers=1) as executor:
futuro = executor.submit(tarefa_lenta)
print(futuro.result(timeout=0.1))
except TimeoutError:
print("chamador: timeout tratado fora do with")
print("fim do programa")O result(timeout=0.1) lança TimeoutError, mas a saída do with executa o equivalente a shutdown(wait=True). Portanto, o trabalhador termina antes de o except externo receber a exceção e imprimir sua mensagem.
O timeout limitou apenas aquela espera por resultado; ele não limitou a duração total do bloco with. Além disso, a saída do contexto não cancela automaticamente o que estava pendente.
Relacione cada situação à consequência correta.
Toque em um item e depois no par correspondente.
Por que um TimeoutError gerado por futuro.result(timeout=...) dentro de um bloco with pode fazer o except externo esperar mais tempo antes de executar?
Escreva pelo menos 80 caracteres (0/80).

Passo 6 de 8
Use um sinal compartilhado para que trabalhadores em threads encerrem seu próprio trabalho de forma cooperativa.
threading.Event é um sinal compartilhado entre quem coordena o trabalho e os trabalhadores. Um Event() novo começa desativado.
parar.set() para ativar o pedido de parada.parar.is_set() entre unidades de trabalho.A mesma instância de Event deve ser passada a todos os trabalhadores que precisam responder ao mesmo pedido.

O coordenador sinaliza uma vez; cada trabalhador decide encerrar quando volta a consultar o mesmo Event.
Dica
Chamar set() não marca o futuro como cancelado e não encerra uma thread à força. É um pedido que só produz efeito quando o código do trabalhador coopera e observa o sinal.
Use is_set() para uma consulta imediata: ele retorna True se o sinal já está ativo.
Use wait(timeout) quando o trabalhador pode aguardar o sinal por até um prazo. Ele retorna True se o Event for ativado durante a espera e False se o prazo terminar sem sinalização. Isso permite decidir se continua para a próxima unidade ou se retorna.
O retorno após detectar o sinal é uma conclusão normal da função.
def trabalhador(parar):
for unidade in range(1, 11):
if parar.is_set():
return "parada cooperativa antes da próxima unidade"
processar_unidade(unidade)
recebeu_sinal = parar.wait(0.2)
if recebeu_sinal:
return "parada cooperativa após a unidade"
return "trabalho concluído"Exemplo
Se parar.wait(0.2) retornar False, nenhum sinal foi ativado nesses 0,2 segundo e o laço pode continuar. Se retornar True, o coordenador ativou o Event e o trabalhador deve seguir sua política de encerramento, como retornar após liberar recursos.
Crie um arquivo Python com o código abaixo e execute-o. O coordenador solicita a parada após um curto intervalo. Não espere uma quantidade exata de unidades impressas: o agendamento das threads pode variar.
Observe que o trabalhador executa sua limpeza em finally e que o valor recuperado por result() é o retorno normal da função.
from concurrent.futures import ThreadPoolExecutor
from threading import Event
import time
def trabalhador(nome: str, parar: Event) -> str:
try:
for unidade in range(1, 21):
if parar.is_set():
return f"{nome}: parada cooperativa antes da unidade {unidade}"
print(f"{nome}: processando unidade {unidade}")
# Aguarda no máximo este intervalo; retorna antes se receber o sinal.
if parar.wait(0.15):
return f"{nome}: parada cooperativa após a unidade {unidade}"
return f"{nome}: concluiu todas as unidades"
finally:
print(f"{nome}: limpeza do trabalhador")
parar = Event()
with ThreadPoolExecutor(max_workers=1) as executor:
futuro = executor.submit(trabalhador, "T1", parar)
try:
time.sleep(0.35)
print("Coordenador: solicitando parada")
parar.set()
resultado = futuro.result()
finally:
# Garante a sinalização mesmo se o coordenador sair por uma exceção.
parar.set()
print("Resultado:", resultado)
print("done():", futuro.done())
print("cancelled():", futuro.cancelled())Dica
A quantidade de mensagens de processamento não é fixa. O ponto importante é que, após a solicitação, o futuro termina com um texto retornado pelo trabalhador; em seguida, done() é True e cancelled() é False.
Para solicitar cooperativamente a parada aos trabalhadores, o coordenador chama parar.____().
Após executar o programa, explique por que o futuro do trabalhador pode ter done() igual a True e cancelled() igual a False, mesmo depois de o coordenador solicitar a parada.
Escreva pelo menos 80 caracteres (0/80).

Passo 7 de 8
Ajuste pontos de espera para que um trabalhador possa voltar a verificar um pedido cooperativo de parada.
Um trabalhador só reage ao pedido de parada quando seu fluxo de execução volta a consultar o Event ou quando está esperando no próprio Event. Portanto, chamar parar.set() não interrompe à força uma função que esteja em uma operação bloqueante qualquer.
Se uma unidade de trabalho demora, há um bloqueio longo ou há limpeza pendente, a resposta pode demorar. Parada cooperativa não oferece prazo rígido de encerramento.
Compare os dois tipos de espera antes de escolher onde posicionar a próxima verificação.

Uma pausa comum termina pelo prazo; Event.wait(timeout) pode terminar pelo prazo ou pela sinalização.
Atenção
Event.set() pode acordar uma thread que está em Event.wait(...). Ele não interrompe time.sleep(), uma chamada de entrada e saída, um cálculo demorado ou outro bloqueio arbitrário. Quando uma API bloqueante oferecer timeout próprio, use um prazo adequado para ela devolver o controle periodicamente.
Nos dois programas a seguir, o coordenador pede a parada depois de pouco tempo. Não compare números exatos: o agendamento varia. Observe apenas a diferença qualitativa: no primeiro caso, o trabalhador percebe o pedido depois de terminar sleep; no segundo, wait pode retornar assim que o sinal é ativado.
Execute este script localmente. Ele é finito e usa somente a biblioteca padrão.
import threading
import time
from concurrent.futures import ThreadPoolExecutor
def trabalhador(parar: threading.Event) -> str:
print("trabalhador: iniciando pausa")
time.sleep(0.6)
if parar.is_set():
return "parada percebida depois da pausa"
return "pausa terminou sem pedido"
parar = threading.Event()
with ThreadPoolExecutor(max_workers=1) as executor:
futuro = executor.submit(trabalhador, parar)
time.sleep(0.2)
print("coordenador: solicitando parada")
parar.set()
print(f"resultado: {futuro.result()}")Agora execute esta versão e compare a ordem das mensagens.
import threading
import time
from concurrent.futures import ThreadPoolExecutor
def trabalhador(parar: threading.Event) -> str:
print("trabalhador: aguardando sinal ou prazo")
recebeu_sinal = parar.wait(timeout=0.6)
if recebeu_sinal:
return "parada percebida durante a espera"
return "prazo da espera terminou"
parar = threading.Event()
with ThreadPoolExecutor(max_workers=1) as executor:
futuro = executor.submit(trabalhador, parar)
time.sleep(0.2)
print("coordenador: solicitando parada")
parar.set()
print(f"resultado: {futuro.result()}")Um trabalhador repete unidades curtas, mas entre elas usa time.sleep(30) para aguardar. Ele deve responder mais cedo a um pedido de parada durante essa espera. Qual mudança preserva a ideia de uma espera de até 30 segundos?
Uma função chama uma operação bloqueante que não consulta o Event e pode esperar indefinidamente. Explique onde está o problema para a parada cooperativa e justifique uma adaptação geral, sem citar uma API específica.
Escreva pelo menos 80 caracteres (0/80).

Passo 8 de 8
Integre espera limitada, cancelamento da fila, parada cooperativa e inspeção final dos futuros em um lote finito.
Em uma política de encerramento completa, o prazo de wait(...) define apenas quanto tempo o coordenador observará o lote inicialmente. Se ainda houver futuros em not_done, o coordenador solicita parada aos trabalhadores ativos com um Event e pede ao executor que cancele o que ainda estiver na fila.
O encerramento completo pode durar mais que esse prazo: uma função que já começou precisa voltar a observar o sinal e retornar por conta própria.
A linha do tempo separa o retorno de wait do fim efetivo do trabalho.

Após o timeout, podem coexistir tarefas canceladas e tarefas ainda em execução cooperando para parar.
Dica
Entre a decisão de encerrar e shutdown(cancel_futures=True), uma tarefa da fila pode começar. Nesse caso, ela não poderá mais ser cancelada e dependerá do sinal cooperativo. Por isso, não baseie sua lógica em uma contagem exata de cancelamentos.
Crie um arquivo, por exemplo encerrar_lote.py, cole o código completo abaixo e execute com python encerrar_lote.py. Ele usa somente a biblioteca padrão. Os nomes e a ordem das mensagens podem variar entre execuções.
from concurrent.futures import ThreadPoolExecutor, wait
from threading import Event
def trabalhar(nome: str, parar: Event, falhar: bool = False) -> str:
for unidade in range(20):
# A espera termina antes do prazo se alguém sinalizar parar.
if parar.wait(timeout=0.1):
return f"{nome}: parada cooperativamente na unidade {unidade}"
if falhar and unidade == 2:
raise RuntimeError(f"{nome}: falha simulada")
return f"{nome}: concluída normalmente"
parar = Event()
executor = ThreadPoolExecutor(max_workers=3)
futuros = {
executor.submit(trabalhar, "longa-1", parar): "longa-1",
executor.submit(trabalhar, "longa-2", parar): "longa-2",
executor.submit(trabalhar, "falha", parar, True): "falha",
executor.submit(trabalhar, "fila-1", parar): "fila-1",
executor.submit(trabalhar, "fila-2", parar): "fila-2",
}
try:
done, not_done = wait(futuros, timeout=0.35)
print(f"Após a espera inicial: {len(done)} concluído(s), "
f"{len(not_done)} ainda não concluído(s).")
if not_done:
print("Solicitando parada cooperativa e cancelamento da fila...")
finally:
# Também protege caminhos inesperados, não apenas o caminho de sucesso.
parar.set()
executor.shutdown(wait=True, cancel_futures=True)
print("\nEstado final:")
for futuro, nome in futuros.items():
if futuro.cancelled():
print(f"- {nome}: cancelada antes de iniciar")
continue
try:
print(f"- {nome}: {futuro.result()}")
except Exception as erro:
print(f"- {nome}: exceção {type(erro).__name__}: {erro}")
wait(..., timeout=0.35) devolve os conjuntos disponíveis sem lançar TimeoutError. No finally, parar.set() solicita que tarefas iniciadas retornem, e shutdown(wait=True, cancel_futures=True) impede novos envios, tenta cancelar a fila e espera as tarefas não canceladas acabarem.
A inspeção ocorre somente depois do shutdown: cada futuro é classificado como cancelado, resultado normal — inclusive retorno por parada cooperativa — ou exceção da função.
Organize a sequência aplicada quando ainda existe trabalho após a observação inicial.
Com base na sua execução, explique: o que o timeout limitou, quais tarefas poderiam ser canceladas e por que o programa pode continuar aguardando depois desse prazo.
Escreva pelo menos 120 caracteres (0/120).
Resumo
wait(..., timeout=...) limita a espera do coordenador e devolve done e not_done; não encerra o trabalho restante.Event.set() é um pedido cooperativo: só tarefas que voltam a consultar o sinal podem parar por esse mecanismo.shutdown(cancel_futures=True) cancela apenas tarefas que ainda não começaram; uma tarefa que iniciou nesse intervalo precisa cooperar.shutdown(wait=True) aguarda tarefas não canceladas, portanto o encerramento total pode ultrapassar a espera inicial.cancelled() antes de result() para distinguir cancelamento, retornos e exceções.Parabéns! Você concluiu: Controlar espera e encerramento de tarefas em executores
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