Multithreading e Multiprocessamento em Python

PythonBeginner
Pratique Agora

Introdução

Entre num mundo onde a revolução industrial remodelou a sociedade, e a agitação da vida da era vitoriana do século XIX molda as ruas de paralelepípedos. Aqui, em meio aos becos nebulosos ladeados pelo barulho de carroças puxadas por cavalos e o zumbido distante da maquinaria das fábricas, vive um personagem cujo talento cativa as multidões – um artista de rua conhecido por sua incrível capacidade de multitarefa.

Nosso artista, chamado Oliver, é famoso por fazer malabarismos com objetos enquanto se equilibra em uma monociclo, toca harmônica e resolve enigmas complexos simultaneamente. Para espanto de seu público, as performances de Oliver são perfeitas e eficientes, um testemunho de seu domínio de atividades concorrentes. Este Lab visa espelhar a proeza de multitarefa de Oliver, mergulhando nas capacidades de multithreading e multiprocessing do Python, garantindo que os codificadores possam gerenciar várias tarefas simultaneamente sem problemas.

Entendendo Threads

Nesta etapa, você aprenderá os fundamentos de threading (threads) em Python. Threading permite que você execute múltiplas operações concorrentemente, fazendo parecer que seu programa está fazendo mais de uma coisa de uma vez, assim como a habilidade de Oliver de fazer malabarismos enquanto anda de bicicleta.

Abra um arquivo chamado simple_threads.py no diretório ~/project com o seguinte conteúdo:

import threading
import time

def print_numbers():
    for i in range(1, 6):
        time.sleep(1)
        print(i)

## Create two threads
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_numbers)

## Start both threads
thread1.start()
thread2.start()

## Wait for both threads to complete
thread1.join()
thread2.join()

print("Done with numbers!")

Execute o código acima no terminal com:

python simple_threads.py

Você deve ver os números sendo impressos intercalados de ambas as threads, seguido por "Done with numbers!":

1
1
2
2
3
3
4
4
5
5
Done with numbers!

Utilizando Multiprocessamento

Agora, vamos usar o multiprocessing (multiprocessamento) para acelerar os cálculos. O multiprocessing em Python permite a execução de múltiplos processos, que podem ser executados em diferentes núcleos de CPU, reduzindo o tempo total necessário para tarefas vinculadas à CPU.

Abra um arquivo chamado process_prime.py no diretório ~/project com o seguinte conteúdo:

from multiprocessing import Process
import math

def is_prime(num):
    """
    Check if a number is prime.
    """
    if num <= 1:
        return False
    for i in range(2, int(math.sqrt(num)) + 1):
        if num % i == 0:
            return False
    return True

def compute_primes(start, end):
    """
    Compute prime numbers within a given range.
    """
    prime_numbers = [num for num in range(start, end) if is_prime(num)]
    print(f"Primes in range {start}-{end}: {prime_numbers}")

processes = []
## Creating two processes
for i in range(0, 20000, 10000):
    ## Create a new process targeting the compute_primes function and passing the range as arguments
    p = Process(target=compute_primes, args=(i, i+10000))
    processes.append(p)
    ## Start the process
    p.start()

## Wait for all processes to finish
for p in processes:
    p.join()

print("Done with prime computation!")

Execute o script no terminal:

python process_prime.py

Isso exibirá os números primos encontrados nos intervalos especificados e exibirá uma mensagem de conclusão:

Primes in range 0-10000: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241,
 ... ...
9851, 9857, 9859, 9871, 9883, 9887, 9901, 9907, 9923, 9929, 9931, 9941, 9949, 9967, 9973]
Primes in range 10000-20000: [10007, 10009, 10037, 10039, 10061, 10067, 10069, 10079, 10091, 10093, 10099, 10103, 10111, 10133, 10139, 10141, 10151, 10159, 10163, 10169, 10177, 10181, 10193,
 ... ...
19739, 19751, 19753, 19759, 19763, 19777, 19793, 19801, 19813, 19819, 19841, 19843, 19853, 19861, 19867, 19889, 19891, 19913, 19919, 19927, 19937, 19949, 19961, 19963, 19973, 19979, 19991, 19993, 19997]
Done with prime computation!

Resumo

Neste laboratório, embarcamos em uma jornada envolvente para desvendar as nuances de threading (threads) e multiprocessing (multiprocessamento) em Python, semelhante às performances em múltiplas camadas dos artistas de rua vitorianos. Começamos fazendo malabarismos com tarefas usando threads e, em seguida, escalamos para processar operações pesadas em todos os núcleos da CPU. Este exercício não apenas demonstrou as capacidades paralelas do Python, mas também revelou como essas técnicas têm significado no mundo real, semelhante às exibições multifacetadas de habilidade do nosso personagem, Oliver.

Sua conclusão deste laboratório deve incluir uma compreensão clara das threads Python para tarefas vinculadas a I/O e processos para tarefas vinculadas à CPU, elevando efetivamente o desempenho da sua codificação e tornando seus aplicativos mais eficientes.