Como Esperar uma Thread Python Terminar

PythonBeginner
Pratique Agora

Introdução

Dominar como esperar a conclusão de threads Python é essencial para construir aplicações robustas e confiáveis. Em programas multi-threaded, a sincronização adequada garante que as operações sejam concluídas na ordem correta e que os recursos sejam utilizados de forma eficiente.

Neste laboratório, você aprenderá como criar threads Python, esperar que elas sejam concluídas e lidar com múltiplas threads. Essas habilidades são fundamentais para o desenvolvimento de aplicações concorrentes que podem realizar múltiplas tarefas simultaneamente, mantendo a sincronização adequada.

Criando Sua Primeira Thread Python

O módulo threading do Python fornece uma maneira simples de criar e gerenciar threads. Nesta etapa, você aprenderá como criar uma thread básica e observar seu comportamento.

Entendendo Threads

Uma thread é um fluxo de execução separado em um programa. Quando você executa um script Python, ele começa com uma única thread chamada thread principal (main thread). Ao criar threads adicionais, seu programa pode realizar múltiplas tarefas simultaneamente.

Threads são úteis para:

  • Executar operações demoradas sem bloquear o programa principal
  • Processar tarefas em paralelo para melhorar o desempenho
  • Lidar com múltiplas conexões de clientes em uma aplicação servidor

Criando uma Thread Simples

Vamos começar criando um script Python simples que demonstra como criar e iniciar uma thread.

  1. Abra um novo arquivo no editor clicando no menu "File", selecionando "New File" e, em seguida, salvando-o como simple_thread.py no diretório /home/labex/project.

  2. Adicione o seguinte código ao arquivo:

import threading
import time

def print_numbers():
    """Function that prints numbers from 1 to 5 with a delay."""
    for i in range(1, 6):
        print(f"Number {i} from thread")
        time.sleep(1)  ## Sleep for 1 second

## Create a thread that targets the print_numbers function
number_thread = threading.Thread(target=print_numbers)

## Start the thread
print("Starting the thread...")
number_thread.start()

## Main thread continues execution
print("Main thread continues to run...")
print("Main thread is doing other work...")

## Sleep for 2 seconds to demonstrate both threads running concurrently
time.sleep(2)
print("Main thread finished its work!")
  1. Salve o arquivo pressionando Ctrl+S ou clicando em "File" > "Save".

  2. Execute o script abrindo um terminal (se ainda não estiver aberto) e executando:

python3 /home/labex/project/simple_thread.py

Você deve ver uma saída semelhante a esta:

Starting the thread...
Main thread continues to run...
Main thread is doing other work...
Number 1 from thread
Number 2 from thread
Main thread finished its work!
Number 3 from thread
Number 4 from thread
Number 5 from thread

Analisando o que Aconteceu

Neste exemplo:

  1. Importamos os módulos threading e time.
  2. Definimos uma função print_numbers() que imprime números de 1 a 5 com um atraso de 1 segundo entre cada um.
  3. Criamos um objeto thread, especificando a função a ser executada usando o parâmetro target.
  4. Iniciamos a thread usando o método start().
  5. A thread principal continuou sua execução, imprimindo mensagens e dormindo por 2 segundos.
  6. Tanto a thread principal quanto nossa thread de números foram executadas simultaneamente, e é por isso que a saída é intercalada.

Observe que a thread principal terminou antes que a thread de números imprimisse todos os seus números. Isso ocorre porque as threads são executadas independentemente e, por padrão, o programa Python sairá quando a thread principal terminar, mesmo que outras threads ainda estejam em execução.

Na próxima etapa, você aprenderá como esperar a conclusão de uma thread usando o método join().

Esperando a Conclusão de uma Thread com join()

Na etapa anterior, você criou uma thread que era executada independentemente da thread principal. No entanto, existem muitas situações em que você precisa esperar que uma thread termine seu trabalho antes de prosseguir com o restante do seu programa. É aqui que o método join() se torna útil.

Entendendo o Método join()

O método join() de um objeto thread bloqueia a thread chamadora (geralmente a thread principal) até que a thread cujo método join() é chamado termine. Isso é essencial quando:

  • A thread principal precisa de resultados de uma thread de trabalho
  • Você precisa garantir que todas as threads sejam concluídas antes de sair do programa
  • A ordem das operações é importante para a lógica da sua aplicação

Criando uma Thread e Esperando sua Conclusão

Vamos modificar nosso exemplo anterior para demonstrar como esperar a conclusão de uma thread usando o método join().

  1. Crie um novo arquivo chamado join_thread.py no diretório /home/labex/project.

  2. Adicione o seguinte código ao arquivo:

import threading
import time

def calculate_sum(numbers):
    """Function that calculates the sum of numbers with a delay."""
    print("Starting the calculation...")
    time.sleep(3)  ## Simulate a time-consuming calculation
    result = sum(numbers)
    print(f"Calculation result: {result}")
    return result

## Create a list of numbers
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

## Create a thread that targets the calculate_sum function
calculation_thread = threading.Thread(target=calculate_sum, args=(numbers,))

## Start the thread
print("Main thread: Starting the calculation thread...")
calculation_thread.start()

## Do some other work in the main thread
print("Main thread: Doing some other work while waiting...")
for i in range(5):
    print(f"Main thread: Working... ({i+1}/5)")
    time.sleep(0.5)

## Wait for the calculation thread to complete
print("Main thread: Waiting for the calculation thread to finish...")
calculation_thread.join()
print("Main thread: Calculation thread has finished!")

## Continue with the main thread
print("Main thread: Continuing with the rest of the program...")
  1. Salve o arquivo e execute-o com o seguinte comando:
python3 /home/labex/project/join_thread.py

Você deve ver uma saída semelhante a esta:

Main thread: Starting the calculation thread...
Starting the calculation...
Main thread: Doing some other work while waiting...
Main thread: Working... (1/5)
Main thread: Working... (2/5)
Main thread: Working... (3/5)
Main thread: Working... (4/5)
Main thread: Working... (5/5)
Main thread: Waiting for the calculation thread to finish...
Calculation result: 55
Main thread: Calculation thread has finished!
Main thread: Continuing with the rest of the program...

A Importância de join()

Neste exemplo:

  1. Criamos uma thread que realiza um cálculo (somando números).
  2. A thread principal fez algum outro trabalho simultaneamente.
  3. Quando a thread principal precisou garantir que o cálculo fosse concluído, ela chamou calculation_thread.join().
  4. O método join() fez com que a thread principal esperasse até que a thread de cálculo terminasse.
  5. Depois que a thread de cálculo foi concluída, a thread principal continuou sua execução.

Este padrão é muito útil quando você precisa garantir que todas as tarefas em threads sejam concluídas antes de prosseguir com o restante do seu programa. Sem join(), a thread principal pode continuar e até mesmo sair antes que as threads de trabalho tenham concluído suas tarefas.

Usando join() com um Timeout

Às vezes, você pode querer esperar por uma thread, mas não indefinidamente. O método join() aceita um parâmetro de timeout opcional que especifica o número máximo de segundos a serem esperados.

Vamos modificar nosso código para demonstrar isso:

  1. Crie um novo arquivo chamado join_timeout.py no diretório /home/labex/project.

  2. Adicione o seguinte código:

import threading
import time

def long_running_task():
    """A function that simulates a very long-running task."""
    print("Long-running task started...")
    time.sleep(10)  ## Simulate a 10-second task
    print("Long-running task completed!")

## Create and start the thread
task_thread = threading.Thread(target=long_running_task)
task_thread.start()

## Wait for the thread to complete, but only for up to 3 seconds
print("Main thread: Waiting for up to 3 seconds...")
task_thread.join(timeout=3)

## Check if the thread is still running
if task_thread.is_alive():
    print("Main thread: The task is still running, but I'm continuing anyway!")
else:
    print("Main thread: The task has completed within the timeout period.")

## Continue with the main thread
print("Main thread: Continuing with other operations...")
## Let's sleep a bit to see the long-running task complete
time.sleep(8)
print("Main thread: Finished.")
  1. Salve o arquivo e execute-o:
python3 /home/labex/project/join_timeout.py

A saída deve ser semelhante a esta:

Long-running task started...
Main thread: Waiting for up to 3 seconds...
Main thread: The task is still running, but I'm continuing anyway!
Main thread: Continuing with other operations...
Long-running task completed!
Main thread: Finished.

Neste exemplo, a thread principal espera por até 3 segundos para que a thread de tarefa seja concluída. Como a tarefa leva 10 segundos, a thread principal continua após o timeout, enquanto a thread de tarefa continua sendo executada em segundo plano.

Essa abordagem é útil quando você deseja dar às threads a chance de serem concluídas, mas precisa continuar independentemente após um certo período de tempo.

Trabalhando com Múltiplas Threads

Em aplicações do mundo real, você frequentemente precisa trabalhar com múltiplas threads simultaneamente. Esta etapa ensinará como criar, gerenciar e sincronizar múltiplas threads em Python.

Criando Múltiplas Threads

Ao lidar com múltiplas tarefas semelhantes, é comum criar múltiplas threads para processá-las simultaneamente. Isso pode melhorar significativamente o desempenho, especialmente para operações vinculadas a I/O (I/O-bound), como baixar arquivos ou fazer requisições de rede.

Vamos criar um exemplo que usa múltiplas threads para processar uma lista de tarefas:

  1. Crie um novo arquivo chamado multiple_threads.py no diretório /home/labex/project.

  2. Adicione o seguinte código:

import threading
import time
import random

def process_task(task_id):
    """Function to process a single task."""
    print(f"Starting task {task_id}...")
    ## Simulate variable processing time
    processing_time = random.uniform(1, 3)
    time.sleep(processing_time)
    print(f"Task {task_id} completed in {processing_time:.2f} seconds.")
    return task_id

## List of tasks to process
tasks = list(range(1, 6))  ## Tasks with IDs 1 through 5

## Create a list to store our threads
threads = []

## Create and start a thread for each task
for task_id in tasks:
    thread = threading.Thread(target=process_task, args=(task_id,))
    threads.append(thread)
    print(f"Created thread for task {task_id}")
    thread.start()

print(f"All {len(threads)} threads have been started")

## Wait for all threads to complete
for thread in threads:
    thread.join()

print("All tasks have been completed!")
  1. Salve o arquivo e execute-o:
python3 /home/labex/project/multiple_threads.py

A saída variará a cada vez devido aos tempos de processamento aleatórios, mas deve ser semelhante a esta:

Created thread for task 1
Starting task 1...
Created thread for task 2
Starting task 2...
Created thread for task 3
Starting task 3...
Created thread for task 4
Starting task 4...
Created thread for task 5
Starting task 5...
All 5 threads have been started
Task 1 completed in 1.23 seconds.
Task 3 completed in 1.45 seconds.
Task 2 completed in 1.97 seconds.
Task 5 completed in 1.35 seconds.
Task 4 completed in 2.12 seconds.
All tasks have been completed!

Entendendo o Fluxo de Execução

Neste exemplo:

  1. Definimos uma função process_task() que simula o processamento de uma tarefa com uma duração aleatória.
  2. Criamos uma lista de IDs de tarefas (1 a 5).
  3. Para cada tarefa, criamos uma thread, armazenamos em uma lista e a iniciamos.
  4. Após iniciar todas as threads, usamos um segundo loop com join() para esperar que cada thread fosse concluída.
  5. Somente após todas as threads serem concluídas, imprimimos a mensagem final.

Este padrão é muito útil quando você tem um lote de tarefas independentes que podem ser processadas em paralelo.

Thread Pool Executors

Para um gerenciamento de threads mais avançado, o módulo concurrent.futures do Python fornece a classe ThreadPoolExecutor. Isso cria um pool de threads de trabalho que podem ser reutilizadas, o que é mais eficiente do que criar e destruir threads para cada tarefa.

Vamos reescrever nosso exemplo usando um thread pool:

  1. Crie um novo arquivo chamado thread_pool.py no diretório /home/labex/project.

  2. Adicione o seguinte código:

import concurrent.futures
import time
import random

def process_task(task_id):
    """Function to process a single task."""
    print(f"Starting task {task_id}...")
    ## Simulate variable processing time
    processing_time = random.uniform(1, 3)
    time.sleep(processing_time)
    print(f"Task {task_id} completed in {processing_time:.2f} seconds.")
    return f"Result of task {task_id}"

## List of tasks to process
tasks = list(range(1, 6))  ## Tasks with IDs 1 through 5

## Create a ThreadPoolExecutor
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    ## Submit all tasks and store the Future objects
    print(f"Submitting {len(tasks)} tasks to the thread pool with 3 workers...")
    future_to_task = {executor.submit(process_task, task_id): task_id for task_id in tasks}

    ## As each task completes, get its result
    for future in concurrent.futures.as_completed(future_to_task):
        task_id = future_to_task[future]
        try:
            result = future.result()
            print(f"Got result from task {task_id}: {result}")
        except Exception as e:
            print(f"Task {task_id} generated an exception: {e}")

print("All tasks have been processed!")
  1. Salve o arquivo e execute-o:
python3 /home/labex/project/thread_pool.py

A saída variará novamente devido aos tempos de processamento aleatórios, mas deve ser semelhante a esta:

Submitting 5 tasks to the thread pool with 3 workers...
Starting task 1...
Starting task 2...
Starting task 3...
Task 2 completed in 1.15 seconds.
Starting task 4...
Got result from task 2: Result of task 2
Task 1 completed in 1.82 seconds.
Starting task 5...
Got result from task 1: Result of task 1
Task 3 completed in 2.25 seconds.
Got result from task 3: Result of task 3
Task 4 completed in 1.45 seconds.
Got result from task 4: Result of task 4
Task 5 completed in 1.67 seconds.
Got result from task 5: Result of task 5
All tasks have been processed!

Benefícios dos Thread Pools

A abordagem do thread pool oferece várias vantagens:

  1. Gerenciamento de Recursos: Ele limita o número de threads que podem ser executadas simultaneamente, evitando o esgotamento dos recursos do sistema.
  2. Agendamento de Tarefas: Ele lida com o agendamento de tarefas automaticamente, iniciando novas tarefas à medida que as threads ficam disponíveis.
  3. Coleta de Resultados: Ele fornece maneiras convenientes de coletar resultados de tarefas concluídas.
  4. Tratamento de Exceções: Torna o tratamento de exceções em threads mais direto.

Em nosso exemplo, definimos max_workers=3, o que significa que apenas 3 threads serão executadas de uma vez, embora tenhamos 5 tarefas. À medida que as threads concluem suas tarefas, elas são reutilizadas para as tarefas restantes.

Os thread pools são particularmente úteis quando você tem muito mais tarefas do que deseja que as threads executem simultaneamente, ou quando as tarefas estão sendo geradas continuamente.

Timeouts de Thread e Daemon Threads

Nesta etapa final, você aprenderá sobre dois conceitos importantes no gerenciamento de threads: definir timeouts e usar daemon threads. Essas técnicas oferecem mais controle sobre como as threads se comportam e interagem com o programa principal.

Trabalhando com Timeouts de Thread

Como você aprendeu na Etapa 2, o método join() aceita um parâmetro de timeout. Isso é útil quando você deseja esperar que uma thread seja concluída, mas apenas até um determinado ponto.

Vamos criar um exemplo mais prático onde implementamos uma função que tenta buscar dados com um timeout:

  1. Crie um novo arquivo chamado thread_with_timeout.py no diretório /home/labex/project.

  2. Adicione o seguinte código:

import threading
import time
import random

def fetch_data(data_id):
    """Simulate fetching data that might take varying amounts of time."""
    print(f"Fetching data #{data_id}...")

    ## Simulate different fetch times, occasionally very long
    fetch_time = random.choices([1, 8], weights=[0.8, 0.2])[0]
    time.sleep(fetch_time)

    if fetch_time > 5:  ## Simulate a slow fetch
        print(f"Data #{data_id}: Fetch took too long!")
        return None
    else:
        print(f"Data #{data_id}: Fetch completed in {fetch_time} seconds!")
        return f"Data content for #{data_id}"

def fetch_with_timeout(data_id, timeout=3):
    """Fetch data with a timeout."""
    result = [None]  ## Using a list to store result from the thread

    def target_func():
        result[0] = fetch_data(data_id)

    ## Create and start the thread
    thread = threading.Thread(target=target_func)
    thread.start()

    ## Wait for the thread with a timeout
    thread.join(timeout=timeout)

    if thread.is_alive():
        print(f"Data #{data_id}: Fetch timed out after {timeout} seconds!")
        return "TIMEOUT"
    else:
        return result[0]

## Try to fetch several pieces of data
for i in range(1, 6):
    print(f"\nAttempting to fetch data #{i}")
    result = fetch_with_timeout(i, timeout=3)
    if result == "TIMEOUT":
        print(f"Main thread: Fetch for data #{i} timed out, moving on...")
    elif result is None:
        print(f"Main thread: Fetch for data #{i} completed but returned no data.")
    else:
        print(f"Main thread: Successfully fetched: {result}")

print("\nAll fetch attempts completed!")
  1. Salve o arquivo e execute-o:
python3 /home/labex/project/thread_with_timeout.py

A saída variará, mas deve ser semelhante a esta:

Attempting to fetch data #1
Fetching data #1...
Data #1: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #1

Attempting to fetch data #2
Fetching data #2...
Data #2: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #2

Attempting to fetch data #3
Fetching data #3...
Data #3: Fetch timed out after 3 seconds!
Main thread: Fetch for data #3 timed out, moving on...
Data #3: Fetch took too long!

Attempting to fetch data #4
Fetching data #4...
Data #4: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #4

Attempting to fetch data #5
Fetching data #5...
Data #5: Fetch completed in 1 seconds!
Main thread: Successfully fetched: Data content for #5

All fetch attempts completed!

Este exemplo demonstra:

  1. Uma função que tenta buscar dados e pode ser lenta
  2. Uma função wrapper que usa threading com um timeout
  3. Como lidar com timeouts de forma elegante e continuar com outras operações

Entendendo Daemon Threads

Em Python, daemon threads são threads que são executadas em segundo plano. A principal diferença entre daemon threads e threads não-daemon é que o Python não esperará que as daemon threads sejam concluídas antes de sair. Isso é útil para threads que executam tarefas em segundo plano que não devem impedir a saída do programa.

Vamos criar um exemplo para demonstrar daemon threads:

  1. Crie um novo arquivo chamado daemon_threads.py no diretório /home/labex/project.

  2. Adicione o seguinte código:

import threading
import time

def background_task(name, interval):
    """A task that runs in the background at regular intervals."""
    count = 0
    while True:
        count += 1
        print(f"{name}: Iteration {count} at {time.strftime('%H:%M:%S')}")
        time.sleep(interval)

def main_task():
    """The main task that runs for a set amount of time."""
    print("Main task: Starting...")
    time.sleep(5)
    print("Main task: Completed!")

## Create two background threads
print("Creating background monitoring threads...")
monitor1 = threading.Thread(target=background_task, args=("Monitor-1", 1), daemon=True)
monitor2 = threading.Thread(target=background_task, args=("Monitor-2", 2), daemon=True)

## Start the background threads
monitor1.start()
monitor2.start()

print("Background monitors started, now starting main task...")

## Run the main task
main_task()

print("Main task completed, program will exit without waiting for daemon threads.")
print("Daemon threads will be terminated when the program exits.")
  1. Salve o arquivo e execute-o:
python3 /home/labex/project/daemon_threads.py

A saída deve ser semelhante a esta:

Creating background monitoring threads...
Background monitors started, now starting main task...
Main task: Starting...
Monitor-1: Iteration 1 at 14:25:10
Monitor-2: Iteration 1 at 14:25:10
Monitor-1: Iteration 2 at 14:25:11
Monitor-1: Iteration 3 at 14:25:12
Monitor-2: Iteration 2 at 14:25:12
Monitor-1: Iteration 4 at 14:25:13
Monitor-1: Iteration 5 at 14:25:14
Monitor-2: Iteration 3 at 14:25:14
Main task: Completed!
Main task completed, program will exit without waiting for daemon threads.
Daemon threads will be terminated when the program exits.

Neste exemplo:

  1. Criamos duas daemon threads que são executadas continuamente, imprimindo mensagens em intervalos regulares.
  2. Definimos daemon=True ao criar as threads, o que as marca como daemon threads.
  3. A thread principal é executada por 5 segundos e depois sai.
  4. Quando a thread principal sai, o programa termina e as daemon threads também são automaticamente terminadas.

Threads Não-Daemon vs. Daemon

Para entender melhor a diferença, vamos criar mais um exemplo que compara daemon e non-daemon threads:

  1. Crie um novo arquivo chamado daemon_comparison.py no diretório /home/labex/project.

  2. Adicione o seguinte código:

import threading
import time

def task(name, seconds, daemon=False):
    """A task that runs for a specified amount of time."""
    print(f"{name} starting {'(daemon)' if daemon else '(non-daemon)'}")
    time.sleep(seconds)
    print(f"{name} finished after {seconds} seconds")

## Create a non-daemon thread that runs for 8 seconds
non_daemon_thread = threading.Thread(
    target=task,
    args=("Non-daemon thread", 8, False),
    daemon=False  ## This is the default, so it's not actually needed
)

## Create a daemon thread that runs for 8 seconds
daemon_thread = threading.Thread(
    target=task,
    args=("Daemon thread", 8, True),
    daemon=True
)

## Start both threads
non_daemon_thread.start()
daemon_thread.start()

## Let the main thread run for 3 seconds
print("Main thread will run for 3 seconds...")
time.sleep(3)

## Check which threads are still running
print("\nAfter 3 seconds:")
print(f"Daemon thread is alive: {daemon_thread.is_alive()}")
print(f"Non-daemon thread is alive: {non_daemon_thread.is_alive()}")

print("\nMain thread is finishing. Here's what will happen:")
print("1. The program will wait for all non-daemon threads to complete")
print("2. Daemon threads will be terminated when the program exits")

print("\nWaiting for non-daemon threads to finish...")
## We don't need to join the non-daemon thread, Python will wait for it
## But we'll explicitly join it for clarity
non_daemon_thread.join()
print("All non-daemon threads have finished, program will exit now.")
  1. Salve o arquivo e execute-o:
python3 /home/labex/project/daemon_comparison.py

A saída deve ser semelhante a esta:

Non-daemon thread starting (non-daemon)
Daemon thread starting (daemon)
Main thread will run for 3 seconds...

After 3 seconds:
Daemon thread is alive: True
Non-daemon thread is alive: True

Main thread is finishing. Here's what will happen:
1. The program will wait for all non-daemon threads to complete
2. Daemon threads will be terminated when the program exits

Waiting for non-daemon threads to finish...
Non-daemon thread finished after 8 seconds
All non-daemon threads have finished, program will exit now.

Observações chave:

  1. Ambas as threads iniciam e são executadas simultaneamente.
  2. Após 3 segundos, ambas as threads ainda estão em execução.
  3. O programa espera que a thread não-daemon termine (após 8 segundos).
  4. A thread daemon ainda está em execução quando o programa sai, mas é terminada.
  5. A thread daemon nunca chega a imprimir sua mensagem de conclusão porque é terminada quando o programa sai.

Quando Usar Daemon Threads

Daemon threads são úteis para:

  • Tarefas de monitoramento em segundo plano
  • Operações de limpeza
  • Serviços que devem ser executados durante a duração do programa, mas não impedi-lo de sair
  • Threads de temporizador que acionam eventos em intervalos regulares

Threads não-daemon são apropriadas para:

  • Operações críticas que devem ser concluídas
  • Tarefas que não devem ser interrompidas
  • Operações que devem ser finalizadas de forma limpa antes que o programa saia

Entender quando usar cada tipo é uma parte importante do projeto de aplicações multi-threaded robustas.

Resumo

Neste laboratório, você aprendeu as técnicas essenciais para trabalhar com threads Python e como esperar que elas sejam concluídas. Aqui está um resumo dos principais conceitos abordados:

  1. Criando e Iniciando Threads: Você aprendeu como criar um objeto thread, especificar a função alvo e iniciar sua execução com o método start().

  2. Esperando por Threads com join(): Você descobriu como usar o método join() para esperar que uma thread seja concluída antes de continuar com o programa principal, garantindo a sincronização adequada.

  3. Trabalhando com Múltiplas Threads: Você praticou a criação e o gerenciamento de múltiplas threads, tanto manualmente quanto usando a classe ThreadPoolExecutor para um gerenciamento de threads mais eficiente.

  4. Timeouts de Thread e Daemon Threads: Você explorou tópicos avançados, incluindo a definição de timeouts para operações de thread e o uso de daemon threads para tarefas em segundo plano.

Essas habilidades fornecem uma base para o desenvolvimento de aplicações multi-threaded em Python. Multi-threading permite que seus programas executem múltiplas tarefas simultaneamente, melhorando o desempenho e a capacidade de resposta, especialmente para operações vinculadas a I/O (I/O-bound).

Ao continuar trabalhando com threads, lembre-se destas melhores práticas:

  • Use threads para tarefas vinculadas a I/O, não para tarefas vinculadas a CPU (considere usar multiprocessing para as últimas)
  • Esteja atento aos recursos compartilhados e use mecanismos de sincronização apropriados
  • Considere usar abstrações de nível superior como ThreadPoolExecutor para gerenciar múltiplas threads
  • Use daemon threads para tarefas em segundo plano que não devem impedir a saída do programa

Com essas habilidades e práticas, você agora está equipado para construir aplicações Python mais eficientes e responsivas usando técnicas de multi-threading.