# Multiprocessamento em Python: execução de processos em paralelo

O multiprocessamento Python permite dividir a carga de trabalho em vários processos e, assim, reduzir o tempo total de execução. Isso representa uma grande vantagem, especialmente quando é necessário realizar muitos cálculos ou quando há grandes conjuntos de dados.

## O que é multiprocessamento Python?

O multiprocessamento em Python refere-se à capacidade de executar vários processos ao mesmo tempo para aproveitar ao máximo o desempenho dos **sistemas com múltiplos núcleos**. Ao contrário dos sistemas de thread único, nos quais as tarefas são executadas sequencialmente, o multiprocessamento permite que diferentes partes do programa sejam executadas em paralelo e independentemente. Cada processo tem o seu próprio espaço de memória e pode ser processado em núcleos de processador separados, o que reduz significativamente o tempo de execução para operações que exigem muitos cálculos ou têm requisitos de tempo rigorosos.

O multiprocessamento Python pode ser utilizado no processamento e análise de dados, por exemplo, para processar grandes conjuntos de dados de forma mais rápida. Também pode ser usado em **simulações e cálculos de modelagem**, como em aplicações científicas, para reduzir os tempos de execução de cálculos complexos. Além disso, o multiprocessamento é usado na área de web scraping para coletar dados de várias páginas da web simultaneamente, bem como no processamento de imagens e visão computacional para melhorar a eficiência das operações de análise.

## Onde é possível implementar o multiprocessamento em Python?

Python oferece várias possibilidades para implementar o multiprocessamento. A seguir, apresentamos três ferramentas comuns: o módulo `multiprocessing`, a biblioteca `concurrent.futures` e o pacote `joblib`.

### O módulo `multiprocessing`

`Multiprocessing` é o **módulo padrão para multiprocessamento em Python**. Com ele, é possível criar processos, trocar dados entre esses processos e realizar sincronizações por meio de bloqueios, filas e outros mecanismos.

```python
import multiprocessing
def task(n):
    result = n * n
    print(f"Result: {result}")
if __name__ == "__main__":
    processes = []
    for i in range(1, 6):
        process = multiprocessing.Process(target=task, args=(i,))
        processes.append(process)
        process.start()
    for process in processes:
        process.join()
```

No exemplo anterior, a classe `multiprocessing.Process` foi usada para criar e iniciar processos que executam a função `task()`. Essa função pega o número transferido e o eleva ao quadrado. Em seguida, cada processo é iniciado e aguarda-se que eles terminem antes de continuar com o programa principal. O resultado é obtido com uma **f-string**, que é um método do Python string format para ligar expressões. A sequência de saída dos resultados não segue uma ordem específica e pode variar em cada execução.

Você também pode criar um pool de processos com Python `multiprocessing` da seguinte maneira:

```python
import multiprocessing
def task(n):
    return n * n
if __name__ == "__main__":
    with multiprocessing.Pool() as pool:
        results = pool.map(task, range(1, 6))
        print(results)  # Output: [1, 4, 9, 16, 25]
```

Com `pool.map()`, a função `task()` é aplicada a uma sequência de dados, e os resultados são recolhidos e devolvidos.

### A biblioteca `concurrent.futures`

O módulo `concurrent.futures` fornece uma interface de alto nível para a execução assíncrona e o processamento paralelo de tarefas. Ele usa o **Pool Executor** para executar tarefas num pool de processos ou threads. Este módulo oferece uma maneira mais simples de lidar com tarefas assíncronas e, em muitos casos, é mais fácil de usar do que o módulo multiprocessing do Python.

```python
import concurrent.futures
def task(n):
    return n * n
with concurrent.futures.ProcessPoolExecutor() as executor:
    futures = [executor.submit(task, i) for i in range(1, 6)]
    for future in concurrent.futures.as_completed(futures):
        print(future.result()) # result in random order
```

O código utiliza o módulo `concurrent.futures` para processar tarefas em paralelo com o `ProcessPoolExecutor`. A função `task(n)` é transferida para os números de 1 a 5. O método `as_completed()` aguarda a conclusão das tarefas e retorna os **resultados em ordem aleatória**.

### `joblib`

`joblib` é uma biblioteca externa do Python concebida para simplificar o processamento em paralelo, por exemplo, para tarefas repetitivas como executar funções com diferentes parâmetros de entrada ou trabalhar com grandes quantidades de dados. As principais funções do `joblib` são paralelizar tarefas, **armazenar em cache os resultados das funções** e otimizar os recursos de memória e computação.

```python
from joblib import Parallel, delayed
def task(n):
    return n * n
results = Parallel(n_jobs=4)(delayed(task)(i) for i in range(1, 11))
print(results) # Output: Results of the function for numbers from 1 to 10
```

Com a expressão `Parallel(n_jobs=4)(delayed(task)(i) for i in range(1, 11))`, inicia-se a execução em paralelo da função `task()` para os números de 1 a 10. `Parallel` está configurado com o argumento `n_jobs=4`, o que indica que podem ser processados até quatro trabalhos em paralelo. Ao chamar `delayed(task)(i)`, cria-se a tarefa que deve ser executada em paralelo para cada número i no intervalo de 1 a 10. Ou seja, a função `task()` é chamada simultaneamente para cada um desses números. O resultado para os números de 1 a 10 é armazenado em `results` e impresso.


This is a markdown version of: [https://www.ionos.com/pt-pt/digitalguide/paginas-web/desenvolvimento-web/multiprocessamento-em-python/](https://www.ionos.com/pt-pt/digitalguide/paginas-web/desenvolvimento-web/multiprocessamento-em-python/) for AI/LLM consumption.