← Python na prática
10 / 10 · 50 MIN

Concorrência, prazos e recuperação de tarefas

Coordena futures sem perder identidade, limita trabalho em espera e distingue timeout de cancelamento.

Estado de execução e resultado aceite

Uma Future representa uma chamada agendada; recebê-la não prova que o trabalho terminou. done inclui conclusão normal, exceção e cancelamento. Obtém o resultado para aplicar a regra de aceitação e trata separadamente falha e CancelledError. Mantém um mapping de cada future para o ID da operação. Ao usar as_completed, os resultados chegam por disponibilidade, não pela posição da entrada; fazer zip com a lista original pode atribuir a resposta ao cliente errado. Num relatório de lote, mostra quantos resultados foram aceites, quantos falharam e quais nem chegaram a executar, mantendo as referências necessárias à retoma.

Expirar a espera não termina a tarefa

result(timeout=2) limita quanto o chamador espera. Se a função já está em execução, pode continuar depois de TimeoutError e cancel pode devolver False. Um timeout local não prova que o destinatário rejeitou uma operação nem que houve rollback. Planeia prazos nas operações de I/O e uma política cooperativa para a tarefa reconhecer a paragem. A saída de with ThreadPoolExecutor espera pelo encerramento, pelo que apanhar o timeout dentro do bloco não garante retorno imediato. shutdown com cancel_futures cancela trabalho ainda não iniciado; as tarefas ativas continuam a precisar de conclusão e tratamento dos efeitos.

Um orçamento para todo o percurso

Define o prazo global com um relógio monotónico e calcula o tempo restante antes de esperar, repetir ou fazer backoff. Três tentativas de cinco segundos com dois intervalos de dois segundos podem consumir dezanove segundos, mesmo quando alguém escreveu catorze no requisito. As operações internas precisam de respeitar o orçamento restante; apenas medir no coordenador não interrompe I/O bloqueado. O timeout de as_completed é contado desde a chamada original, sem renovar automaticamente a cada next. Para auditoria entre sistemas usa timestamps adequados, mas não interpretes o valor absoluto do relógio monotónico como uma data UTC.

Capacidade de execução e capacidade de admissão

max_workers limita tarefas ativas, mas não a lista de futures que o produtor decide criar. Submeter milhões de itens de uma vez pode esgotar memória antes de os workers consumirem a fila. Limita trabalho admitido, liberta resultados já tratados e evita transformar primeiro todo o input numa lista. Em Python 3.14, buffersize em Executor.map limita tarefas submetidas cujos resultados ainda não foram emitidos; não existe em 3.13. chunksize não substitui esse controlo num executor de threads. Mede também o tamanho de cada resultado: um número limitado de objetos pode continuar demasiado grande para o orçamento.

Dependências que permitem progresso

Desenha o grafo de dependências antes de aumentar threads. Num pool com um worker, uma tarefa que espera por uma subtarefa submetida ao mesmo pool ocupa precisamente a capacidade necessária para a subtarefa começar. Move a coordenação para fora do worker ou executa diretamente uma fase que deve ser sequencial. Usa ensaios limitados em vez de criar um deadlock permanente na suite. Para escolher threads ou processos, caracteriza a carga: no CPython convencional com GIL, código Python CPU-bound não ganha automaticamente paralelismo com mais threads. I/O e builds free-threaded têm condições diferentes que exigem medição e compatibilidade explícitas.

from concurrent.futures import ThreadPoolExecutor, as_completed

def inspect_report(report_id):
    if report_id == "R-19":
        raise ValueError("missing required field")
    return {"accepted": True}

outcomes = {}
with ThreadPoolExecutor(max_workers=2) as pool:
    pending = {pool.submit(inspect_report, key): key
               for key in ("R-18", "R-19", "R-20")}
    for future in as_completed(pending):
        key = pending[future]
        try:
            response = future.result()
        except ValueError:
            outcomes[key] = "invalid"
        else:
            outcomes[key] = "accepted" if response.get("accepted") is True else "rejected"
assert outcomes == {"R-18": "accepted", "R-19": "invalid", "R-20": "accepted"}
print("3 correlated outcomes; 1 validation failure")
NA PRÁTICA

O exemplo usa apenas três tarefas locais, portanto o conjunto admitido é explicitamente pequeno. A ordem de conclusão pode mudar e o resultado continua associado ao ID correto. Não o generalizes para milhões de IDs sem limitar submissão e retenção. Acrescenta num ensaio separado uma tarefa cooperativa e confirma que timeout da espera não equivale a cancelamento.

Armadilhas comuns

Contar done como sucesso; associar por posição resultados de as_completed; confundir timeout e rollback; submeter tudo antes de limitar; usar buffersize em 3.13; esperar por subtarefas no único worker; prometer paralelismo sem caracterizar a carga.

Tópicos relacionados: Funções pequenas, contratos claros · Erros e ficheiros com contexto · Automação observável e repetível · Subprocessos, caminhos e resultados verificáveis

Leva esta ideia contigo

Concorrência útil preserva identidade e resultados, admite apenas trabalho que consegue sustentar e oferece uma política explícita para prazos, falhas e encerramento.

Criar conta

Referência: Python 3.14: Launching parallel tasks · Python 3.14; DR Python 2026.3

Python® é uma marca registada de Python Software Foundation. A dr.pt é uma plataforma de preparação independente e não está afiliada, associada, patrocinada, autorizada nem aprovada por Python Software Foundation. Os conteúdos e as perguntas são originais, não são perguntas oficiais de exame, e concluir os nossos testes não atribui nem garante qualquer certificação. Os nomes são usados apenas para identificar o tema. Todas as outras marcas pertencem aos respetivos titulares.