Trilha de aprendizado · Nível 13 · Tutorial 2

Executar tarefas com ThreadPoolExecutor

Ao concluir, você será capaz de executar funções independentes em um conjunto de threads e recuperar seus resultados ou falhas sem perder a associação com as entradas.

  • Nível: Intermediário
  • Duração: 20 min
  • 7 passos
Executar tarefas com ThreadPoolExecutor

O que você vai percorrer

  1. Criar um conjunto de trabalhadores Entenda o papel do executor e delimite um conjunto de threads reutilizáveis com um limite explícito. 2 min
  2. Submeter uma função e obter seu resultado Agende uma chamada em um trabalhador e recupere seu retorno por meio de um Future. 3 min
  3. Coletar tarefas conforme ficam prontas Submeta um conjunto pequeno de tarefas antes de esperar e use as_completed para observar os resultados à medida que cada Future termina. 3 min
  4. Manter cada resultado ligado à sua entrada Associe cada Future à entrada que originou a tarefa para produzir uma saída correta mesmo quando as conclusões chegam em outra ordem. 2 min
  5. Recuperar falhas sem perder as outras tarefas Trate falhas de tarefas individuais durante a coleta, mantendo a associação entre cada Future e sua entrada. 3 min
  6. Limitar submissões com lotes finitos Processe uma fonte de entradas em lotes para limitar quantas tarefas ficam submetidas de uma vez, mantendo a associação entre cada Future e sua entrada. 4 min
  7. Aplicar o fluxo completo e conferir os resultados Execute um script autocontido que processa entradas em lotes, associa cada conclusão à entrada original e registra sucessos e falhas. 3 min

O que você vai aprender

  • Criar um ThreadPoolExecutor com um limite explícito de trabalhadores.
  • Submeter funções e recuperar resultados por meio de Future.
  • Processar conclusões com as_completed e associá-las às entradas correspondentes.
  • Limitar a quantidade de submissões pendentes usando lotes finitos.

Antes de começar

  • Escolher entre threads, processos e asyncio
  • Tratar exceções com try, except, else e finally
  • Criar gerenciadores de contexto com classes
  • Limitar o consumo de iteradores com itertools

Passo 1 de 7

Criar um conjunto de trabalhadores

Entenda o papel do executor e delimite um conjunto de threads reutilizáveis com um limite explícito.

Executor, tarefas e trabalhadores

Quem organiza o trabalho?

Um executor recebe chamadas para serem agendadas e as distribui entre trabalhadores disponíveis. Neste tutorial, cada chamada representará uma consulta independente identificada por uma entrada, como um código de produto.

O ThreadPoolExecutor, de concurrent.futures, mantém um conjunto de threads trabalhadoras que pode ser reutilizado para várias tarefas. A tarefa é a chamada a executar; a thread é o trabalhador que a executa. Não são a mesma coisa.

Fluxo do conjunto de threads

As entradas originam tarefas. O executor organiza essas tarefas para um número limitado de threads trabalhadoras.

Diagrama com várias entradas chegando a um executor central, que as encaminha para três threads trabalhadoras reutilizáveis.

As tarefas aguardam ou são executadas pelos trabalhadores do conjunto; uma tarefa não cria necessariamente uma thread própria.

Definir o limite e o ciclo de vida

`max_workers` é um teto

Crie o executor com um valor explícito de max_workers. Esse valor define o máximo de threads trabalhadoras: não significa que todas serão criadas imediatamente nem que estarão sempre ocupadas.

Use with para delimitar o ciclo de vida do executor. Na saída normal do bloco, o contexto espera as tarefas já submetidas terminarem antes de continuar. Vamos aprender a submeter essas tarefas no próximo passo.

Estrutura inicial do executor

Este é o ponto de partida do cenário de consultas simuladas.

python
from concurrent.futures import ThreadPoolExecutor
import time


def consultar(codigo: str) -> str:
    time.sleep(0.2)  # simula uma espera bloqueante curta
    return f"consulta concluída: {codigo}"


codigos = ["A10", "B20", "C30"]

with ThreadPoolExecutor(max_workers=3) as executor:
    # As chamadas serão agendadas aqui no próximo passo.
    pass

print("Executor encerrado")

Checagem rápida

Escolha a configuração

Você precisa de um executor com, no máximo, 2 threads trabalhadoras. Qual opção atende ao requisito?

O que o `with` garante aqui

Saída do contexto

Verdadeiro ou falso: ao sair normalmente do bloco with ThreadPoolExecutor(...), o programa pode continuar enquanto tarefas já submetidas ainda estão em execução.

Resumo

Base montada

Você já pode preparar o ambiente para executar consultas independentes.

  • ThreadPoolExecutor organiza chamadas para um conjunto reutilizável de threads.
  • Tarefas são chamadas a executar; threads são os trabalhadores que as executam.
  • max_workers é o limite máximo de trabalhadores, não uma garantia de ocupação imediata.
  • with delimita o ciclo de vida e, na saída normal, espera as tarefas submetidas terminarem.

Passo 2 de 7

Submeter uma função e obter seu resultado

Agende uma chamada em um trabalhador e recupere seu retorno por meio de um Future.

Submit agenda; não chama no mesmo instante

Função e argumentos separados

executor.submit recebe a função e seus argumentos separadamente. Ele agenda a chamada para um trabalhador e devolve imediatamente um objeto Future.

Use executor.submit(consultar, "item-42"), e não executor.submit(consultar("item-42")). Na segunda forma, consultar(...) é executada antes de submit, na thread que está fazendo a submissão.

Três momentos da mesma chamada

A chamada é agendada primeiro; a função roda no trabalhador; o retorno só é recuperado quando o programa pede o resultado.

Linha do tempo mostrando o programa principal submetendo a função consultar com um identificador, um trabalhador executando a consulta e um Future devolvendo o resultado ao programa principal.

submit devolve um Future; result() recupera o retorno da função quando ele estiver disponível.

Um Future representa a execução

Resultado ainda não é o valor

O valor retornado por submit é um Future de concurrent.futures, não o retorno de consultar.

Um Future representa uma execução que pode estar pendente, em andamento ou já concluída quando você a observa. Para obter o retorno da função, chame future.result().

Dica

A espera acontece em result()

Se a função ainda estiver rodando, future.result() bloqueia a thread que fez essa chamada até haver um retorno disponível. Se o trabalho já terminou, ele devolve o retorno imediatamente.

Execute uma chamada local

Script autocontido

Crie um arquivo chamado future_simples.py, copie o código e execute python future_simples.py no seu terminal. A pausa é apenas uma simulação local de uma operação que demora um pouco.

future_simples.py

python
from concurrent.futures import ThreadPoolExecutor
import time


def consultar(identificador: str) -> str:
    print(f"Trabalhador: consultando {identificador}")
    time.sleep(1)
    return f"resultado de {identificador}"


with ThreadPoolExecutor(max_workers=2) as executor:
    future = executor.submit(consultar, "item-42")
    print(f"Recebi um Future: {future}")

    resultado = future.result()
    print(f"Recebi o resultado: {resultado}")

Exemplo

O que observar

A primeira mensagem do programa mostra uma representação de Future, com detalhes que podem variar. A última mensagem mostra o texto retornado pela função: resultado de item-42.

A ordem exata das duas mensagens iniciais pode variar, pois programa principal e trabalhador são threads diferentes. O ponto importante é: a variável future não contém o texto de retorno; resultado contém, após future.result().

Confira a passagem para submit

Complete a submissão

Para agendar consultar com o identificador "item-42", complete:

future = executor.submit(_____, "item-42")

Relate o que você verificou

Future ou resultado?

Depois de executar o script, explique a diferença observada entre o valor impresso para future e o valor impresso para resultado. Mencione qual linha pode esperar pela conclusão da consulta.

Escreva pelo menos 60 caracteres (0/60).

Passo 3 de 7

Coletar tarefas conforme ficam prontas

Submeta um conjunto pequeno de tarefas antes de esperar e use as_completed para observar os resultados à medida que cada Future termina.

Duas fases: submeter e coletar

Primeiro crie o trabalho; depois observe as conclusões

Para permitir sobreposição, faça duas fases distintas:

  1. Submissão: chame submit para todas as tarefas de um conjunto pequeno e finito, guardando os Futures.
  2. Coleta: percorra esses Futures quando eles forem concluídos.

Se você chamar future.result() logo após cada submit, a thread principal espera aquela tarefa terminar antes de submeter a próxima. Assim, o conjunto de trabalhadores não recebe várias tarefas para coordenar.

Submissão imediata x submissão seguida de coleta

Diagrama comparando, à esquerda, uma thread principal que submete uma tarefa e espera seu resultado repetidamente e, à direita, uma thread principal que submete três tarefas ao pool antes de coletar os resultados conforme terminam.

Esperar após cada submissão serializa esse fluxo; submeter antes de coletar permite que os trabalhadores avancem em tarefas independentes.

Coletar pela conclusão

`as_completed` entrega Future concluídos

as_completed(futures) recebe uma coleção de Futures e produz cada Future assim que ele fica concluído. Ele não entrega diretamente o valor retornado pela função: ainda é necessário chamar future.result().

A ordem produzida não é a ordem de submissão. Percorrer uma lista de futuros na ordem original pode fazer você esperar pelo primeiro, mesmo que os seguintes já tenham terminado. Com as_completed, você observa primeiro o que estiver disponível.

Script local completo

Salve como coletar.py e execute com python coletar.py. As pausas apenas simulam tarefas independentes; a sequência exibida pode variar.

python
from concurrent.futures import ThreadPoolExecutor, as_completed
import time


def consultar(nome: str, demora: float) -> str:
    time.sleep(demora)
    return f"consulta concluída: {nome}"


consultas = [
    ("produto-A", 0.30),
    ("produto-B", 0.10),
    ("produto-C", 0.20),
]

with ThreadPoolExecutor(max_workers=3) as executor:
    # Fase 1: todas as chamadas são submetidas antes da coleta.
    futuros = [
        executor.submit(consultar, nome, demora)
        for nome, demora in consultas
    ]

    # Fase 2: cada Future é entregue quando fica concluído.
    for futuro in as_completed(futuros):
        resultado = futuro.result()
        print(resultado)

A ordem não é um contrato

Durações simuladas não determinam a saída

Neste exemplo, é comum que produto-B apareça antes porque sua pausa é menor. Porém, não use isso como regra do programa: o agendamento depende do ambiente e dois ou mais futuros podem já estar concluídos quando a coleta começar.

Portanto, trate a ordem de as_completed como ordem de disponibilidade, não como uma sequência fixa para comparar em testes ou para combinar posições de listas.

Qual é o papel de `result()` neste laço?

No trecho for futuro in as_completed(futuros): resultado = futuro.result(), o que futuro.result() faz?

Explique o fluxo

Diagnóstico de um padrão sequencial

Explique por que este padrão não permite a sobreposição pretendida entre três tarefas:

for nome, demora in consultas:
    futuro = executor.submit(consultar, nome, demora)
    print(futuro.result())

Indique também a mudança geral necessária.

Escreva pelo menos 80 caracteres (0/80).

Passo 4 de 7

Manter cada resultado ligado à sua entrada

Associe cada Future à entrada que originou a tarefa para produzir uma saída correta mesmo quando as conclusões chegam em outra ordem.

O Future é a ligação entre entrada e conclusão

Não use a posição de chegada

as_completed entrega os futuros na ordem em que terminam, não na ordem em que foram submetidos. Por isso, associar a primeira conclusão à primeira entrada é frágil: uma tarefa posterior pode terminar antes.

Ao submeter cada tarefa, guarde o Future como chave de um dicionário e a entrada correspondente como valor. Quando esse mesmo Future aparecer na coleta, ele permitirá recuperar sua origem.

Rastreando cada tarefa

Cada seta de retorno passa pelo mesmo Future criado na submissão.

Diagrama mostrando as entradas alfa, beta e gama conectadas a Futures distintos; os resultados retornam fora da ordem, mas cada Future aponta para sua entrada de origem.

A ordem de conclusão muda; o vínculo Future → entrada não.

Crie o mapeamento na submissão

Future como chave, entrada como valor

A compreensão cria uma associação para cada chamada submetida.

python
from concurrent.futures import ThreadPoolExecutor, as_completed
import time


def consultar(entrada: str, demora: float) -> str:
    time.sleep(demora)
    return f"resposta para {entrada}"


entradas = [
    ("produto-A", 0.3),
    ("produto-B", 0.1),
    ("produto-C", 0.2),
]

with ThreadPoolExecutor(max_workers=3) as executor:
    futuro_para_entrada = {
        executor.submit(consultar, entrada, demora): entrada
        for entrada, demora in entradas
    }

    for futuro in as_completed(futuro_para_entrada):
        entrada = futuro_para_entrada[futuro]
        resultado = futuro.result()
        print(f"{entrada}: {resultado}")

Dica

Leia a associação em duas etapas

Na compreensão, submit(...) produz a chave Future; entrada é o valor guardado. No laço, futuro_para_entrada[futuro] recupera a entrada antes de futuro.result() obter o retorno.

Colete sem supor uma ordem

A saída é produzida pelo coletor

Neste cenário, a ordem provável de conclusão é produto-B, produto-C, produto-A, porque as esperas são diferentes. A ordem exata não deve ser tratada como contrato, mas cada linha continuará correta porque o coletor consulta a entrada pelo Future concluído.

Evite emparelhar uma lista de entradas com resultados que chegam por as_completed: as posições pertencem a ordens diferentes.

Associe cada conclusão à origem

Considere que as tarefas foram submetidas na ordem A, B, C, mas as_completed as entregou na ordem B, C, A. Relacione cada Future concluído à entrada correta.

Toque em um item e depois no par correspondente.

Passo 5 de 7

Recuperar falhas sem perder as outras tarefas

Trate falhas de tarefas individuais durante a coleta, mantendo a associação entre cada Future e sua entrada.

Conclusão não é sinônimo de sucesso

Um Future pode concluir com erro

Um Future concluído pode guardar um retorno ou uma exceção. Ao chamar future.result(), o retorno é entregue normalmente; se a função falhou, a mesma exceção é propagada no ponto dessa chamada.

Por isso, ao coletar com as_completed, coloque o result() dentro de try e except. O mapeamento Future → entrada permite informar exatamente qual entrada falhou, mesmo sem retorno da função.

Do Future à saída correspondente

A coleta identifica a entrada pelo Future antes de recuperar seu resultado.

Diagrama mostrando três entradas ligadas a três Futures; dois Futures levam a resultados de sucesso e um Future leva a uma exceção, todos identificados pelas entradas originais.

Cada Future concluído pode levar a um resultado ou a uma exceção; o dicionário preserva a ligação com a entrada.

Atenção

Não deixe uma falha interromper a coleta

Se result() lançar uma exceção sem tratamento dentro do laço, a coleta é interrompida naquele ponto. As outras funções já submetidas não são paradas automaticamente, mas seus resultados ou falhas podem deixar de ser observados pelo código.

Execute uma falha controlada

Prática local

Crie um arquivo, por exemplo coletar_falhas.py, cole o código e execute com python coletar_falhas.py. A entrada "produto-inexistente" foi programada para falhar; as demais devem produzir resultados.

Coletar sucessos e falhas por entrada

O except ValueError fica ao redor de future.result(), que é o ponto onde a falha do trabalhador aparece no chamador.

python
from concurrent.futures import ThreadPoolExecutor, as_completed
import time


def consultar(entrada: str) -> str:
    time.sleep(0.2)
    if entrada == "produto-inexistente":
        raise ValueError("produto não encontrado")
    return f"dados de {entrada}"


entradas = ["produto-10", "produto-inexistente", "produto-30"]

with ThreadPoolExecutor(max_workers=2) as executor:
    futuros_para_entrada = {
        executor.submit(consultar, entrada): entrada
        for entrada in entradas
    }

    for future in as_completed(futuros_para_entrada):
        entrada = futuros_para_entrada[future]
        try:
            resultado = future.result()
        except ValueError as erro:
            print(f"FALHA em {entrada}: {erro}")
        else:
            print(f"SUCESSO em {entrada}: {resultado}")

Dica

A ordem pode variar

A ordem das linhas não é garantida: as_completed entrega cada Future quando ele termina. O importante é que cada linha usa a entrada recuperada pelo próprio Future, e não uma posição da lista.

Propagação e saída do contexto

O que acontece se a exceção escapar?

Se você remover o try e o except, future.result() propagará a exceção para fora do bloco with. Antes de a exceção continuar para o chamador, a saída do contexto ainda espera as tarefas já submetidas terminarem.

Essa espera não recupera automaticamente as exceções guardadas nos outros Futures. Para relatar sucesso ou falha de cada tarefa, recupere cada Future durante a coleta.

Verifique a ideia

Se future.result() propagar uma exceção para fora do with, as outras tarefas já submetidas são automaticamente interrompidas.

Confira sua observação

Relate a execução

Após executar o script, qual entrada provocou a falha controlada? O que aconteceu com as outras entradas? Explique também a diferença entre a exceção em result() e a espera na saída do with.

Escreva pelo menos 60 caracteres (0/60).

Passo 6 de 7

Limitar submissões com lotes finitos

Processe uma fonte de entradas em lotes para limitar quantas tarefas ficam submetidas de uma vez, mantendo a associação entre cada Future e sua entrada.

Trabalhadores não limitam a fila de submissões

Dois limites diferentes

max_workers limita quantas funções podem executar ao mesmo tempo. Ele não limita quantas chamadas você pode enviar com submit e deixar aguardando no executor.

Por exemplo, com max_workers=2, é possível submeter 10.000 tarefas: apenas até duas executam simultaneamente, mas as demais podem ficar pendentes. Para limitar também as tarefas submetidas, processe a fonte em lotes finitos.

Submeter tudo × submeter por lotes

Compare os dois fluxos.

Diagrama comparando dois trabalhadores com uma longa fila de muitas tarefas pendentes e dois trabalhadores recebendo apenas um pequeno lote de tarefas por vez.

À esquerda, poucos trabalhadores e muitas submissões acumuladas. À direita, o lote atual é coletado antes de enviar o próximo.

Dica

Limite do lote

Com lote de tamanho B, há no máximo B tarefas submetidas pertencentes ao lote atual. Já a execução simultânea continua limitada por max_workers.

Consumir, submeter e coletar um lote

Um único iterador, vários lotes

Crie o iterador da fonte uma única vez. Em cada repetição, islice consome dele apenas os próximos itens do lote. Submeta somente esse lote, crie o mapeamento Future → entrada e colete todas as conclusões com as_completed antes de buscar novas entradas.

O mesmo executor permanece ativo durante todo o processamento, pois o with envolve o laço de lotes.

Processamento completo por lotes

Execute este script localmente. O último lote tem apenas um item, e item-5 falha de forma controlada.

python
from concurrent.futures import ThreadPoolExecutor, as_completed
from itertools import islice
from time import sleep


def consultar(entrada: str) -> str:
    sleep(0.2)
    if entrada == "item-5":
        raise ValueError("entrada indisponível")
    return f"resultado de {entrada}"


def processar_em_lotes(entradas: list[str], tamanho_lote: int) -> None:
    iterador = iter(entradas)

    with ThreadPoolExecutor(max_workers=2) as executor:
        while True:
            lote = list(islice(iterador, tamanho_lote))
            if not lote:
                break

            futuro_para_entrada = {
                executor.submit(consultar, entrada): entrada
                for entrada in lote
            }

            for futuro in as_completed(futuro_para_entrada):
                entrada = futuro_para_entrada[futuro]
                try:
                    resultado = futuro.result()
                    print(f"SUCESSO | {entrada}: {resultado}")
                except ValueError as erro:
                    print(f"FALHA   | {entrada}: {erro}")


processar_em_lotes(
    ["item-1", "item-2", "item-3", "item-4", "item-5"],
    tamanho_lote=2,
)

A sequência de um lote

Ordene o fluxo

Coloque as etapas de processamento de um lote na ordem correta.

  1. Consumir e submeter o próximo lote.
  2. Consumir do iterador as próximas entradas com islice.
  3. Percorrer as conclusões com as_completed e chamar result em cada Future.
  4. Submeter as entradas do lote e criar o dicionário Future → entrada.

Consequências do limite por lote

O que o lote controla — e o que não controla

Ao terminar o for futuro in as_completed(...), a variável futuro_para_entrada será substituída no próximo ciclo; assim, o programa não precisa manter os Futures de lotes anteriores.

Isso não impede que você acumule resultados em outra lista ou estrutura. Se fizer isso, esse histórico continuará crescendo conforme sua escolha.

Há uma troca: como o código espera o lote inteiro, um trabalhador pode ficar ocioso perto do fim se restar apenas uma tarefa lenta. Ainda assim, o padrão é simples e dá um limite claro às submissões.

Explique o limite

Em suas palavras, qual é a diferença entre max_workers=2 e tamanho_lote=2? Explique também por que o próximo lote só é consumido após a coleta completa do atual.

Escreva pelo menos 80 caracteres (0/80).

Passo 7 de 7

Aplicar o fluxo completo e conferir os resultados

Execute um script autocontido que processa entradas em lotes, associa cada conclusão à entrada original e registra sucessos e falhas.

Fluxo completo em lotes

O padrão reunido

Neste fluxo, um único ThreadPoolExecutor atende toda a fonte, mas cada lote é totalmente coletado antes do próximo ser submetido. Para cada lote, o programa cria o mapeamento Future → entrada, usa as_completed para receber conclusões e chama result() para obter o retorno ou a exceção.

A ordem das linhas pode variar. O critério de correção é outro: cada entrada deve aparecer uma vez, como sucesso ou falha.

Estrutura do processamento

O diagrama mostra onde está o limite de trabalhadores e onde está o limite de submissões pendentes.

Diagrama com uma fonte de entradas dividida em lotes; cada lote é submetido a um executor com três threads, seus Futures são associados às entradas e os resultados ou falhas são coletados antes do lote seguinte.

max_workers limita threads em execução; TAMANHO_LOTE limita quantas tarefas são submetidas por vez.

Dica

Dois limites diferentes

Altere MAX_WORKERS para mudar o máximo de threads trabalhadoras. Altere TAMANHO_LOTE para mudar o máximo de tarefas submetidas ainda não concluídas em cada rodada. Um valor não substitui o outro.

Execute o script localmente

Prepare a execução

Crie um arquivo chamado lotes_threads.py, copie o código completo abaixo e execute no terminal com:

python lotes_threads.py

Ele usa apenas a biblioteca padrão. A falha de pedido-5 é intencional.

lotes_threads.py

Script autocontido: processa sete entradas em lotes de três, com uma falha controlada.

python
from concurrent.futures import ThreadPoolExecutor, as_completed
from itertools import islice
from time import sleep

MAX_WORKERS = 3
TAMANHO_LOTE = 3


def consultar(entrada: str) -> str:
    sleep(0.2)  # simula uma operação bloqueante curta
    if entrada == "pedido-5":
        raise ValueError("pedido inválido")
    return f"resposta para {entrada}"


def processar_em_lotes(entradas: list[str]) -> None:
    fonte = iter(entradas)

    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
        while True:
            lote = list(islice(fonte, TAMANHO_LOTE))
            if not lote:
                break

            future_para_entrada = {
                executor.submit(consultar, entrada): entrada
                for entrada in lote
            }

            for future in as_completed(future_para_entrada):
                entrada = future_para_entrada[future]
                try:
                    resultado = future.result()
                except ValueError as erro:
                    print(f"FALHA   | {entrada}: {erro}")
                else:
                    print(f"SUCESSO | {entrada}: {resultado}")


entradas = [
    "pedido-1",
    "pedido-2",
    "pedido-3",
    "pedido-4",
    "pedido-5",
    "pedido-6",
    "pedido-7",
]

processar_em_lotes(entradas)

Confira o que aconteceu

O que verificar na saída

Você deve encontrar sete linhas no total: seis iniciadas por SUCESSO e uma iniciada por FALHA, identificando pedido-5. As linhas podem surgir em ordem diferente da lista de entradas.

Com lote de tamanho 3 e sete entradas, o programa processa lotes de 3, 3 e 1 item. Portanto, o último lote incompleto também é submetido e coletado.

Relate sua execução

Após executar, informe: os valores de MAX_WORKERS e TAMANHO_LOTE; o total de sucessos e falhas; a entrada que falhou; e por que o código mantém a associação correta e não submete toda a fonte de uma vez.

Escreva pelo menos 120 caracteres (0/120).

Síntese e conclusão

Resumo

Checklist do padrão

Use este fluxo quando houver chamadas independentes e você precisar acompanhar cada uma sem acumular trabalho pendente para toda a fonte.

  • Crie um ThreadPoolExecutor com max_workers explícito e mantenha-o em um bloco with.
  • Envie a função e seus argumentos com submit; guarde o Future retornado.
  • Associe cada Future à entrada em um dicionário antes da coleta.
  • Use as_completed para reagir às conclusões, chamando future.result() dentro de try/except para registrar sucessos e falhas.
  • Consuma a fonte em lotes finitos: submeta e colete um lote inteiro antes de criar o próximo.
  • Não suponha ordem de saída; confira que cada entrada foi registrada exatamente uma vez.

Tutorial concluído

Parabéns! Você concluiu: Executar tarefas com ThreadPoolExecutor

Muito bem! Você consegue submeter tarefas, coletar resultados e falhas fora da ordem de envio, preservar a ligação com cada entrada e limitar as submissões pendentes.

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