Titre : Comment gérer les processus parallèles avec multiprocessing


Introduction

Quand un programme Python doit exécuter plusieurs tâches lourdes ou indépendantes, il est souvent utile de les exécuter en parallèle. Le module intégré multiprocessing permet justement d’exploiter les cœurs multiples de votre processeur, en lançant plusieurs processus qui s’exécutent en parallèle au lieu de manière séquentielle.

Contrairement au multithreading limité par le GIL (Global Interpreter Lock), le multiprocessing lance de vrais processus système, chacun avec sa propre mémoire. C’est une solution idéale pour les tâches CPU-intensives, comme le calcul scientifique, le traitement d’images ou le rendu de jeux.

Dans cet article, vous allez découvrir comment fonctionne multiprocessing, comment créer des processus, gérer des files de communication, synchroniser les tâches, et bien plus.


1. Pourquoi utiliser multiprocessing ?

Le module multiprocessing permet de :

  • répartir des calculs gourmands sur plusieurs cœurs de CPU,
  • accélérer des boucles lentes,
  • exécuter plusieurs tâches indépendantes en parallèle.

Exemple d’usage : traitement de plusieurs fichiers en simultané, génération d’images, traitement de données lourdes…


2. Lancer un processus simple

Voici un exemple de base qui crée un processus parallèle :

import multiprocessing

def afficher():
    print("Processus exécuté !")

if __name__ == '__main__':
    p = multiprocessing.Process(target=afficher)
    p.start()
    p.join()

  • start() lance le processus.
  • join() bloque jusqu’à ce que le processus soit terminé.

3. Passer des arguments à un processus

Vous pouvez transmettre des arguments à une fonction via args :

def calcul(nom):
    print(f"{nom} est en cours...")

if __name__ == '__main__':
    p = multiprocessing.Process(target=calcul, args=("Tâche 1",))
    p.start()
    p.join()

4. Lancer plusieurs processus en boucle

Voici comment lancer plusieurs processus à la fois :

def travail(n):
    print(f"Travail #{n} lancé")

if __name__ == '__main__':
    processus = []

    for i in range(5):
        p = multiprocessing.Process(target=travail, args=(i,))
        processus.append(p)
        p.start()

    for p in processus:
        p.join()

5. Utiliser un Pool de processus

Le module propose aussi une abstraction avec Pool pour lancer plusieurs tâches facilement :

from multiprocessing import Pool

def carre(n):
    return n * n

if __name__ == '__main__':
    with Pool(4) as pool:
        resultats = pool.map(carre, [1, 2, 3, 4, 5])
        print(resultats)

  • map() applique la fonction à tous les éléments de la liste en parallèle.

6. Partager des données entre processus

Puisque chaque processus a sa propre mémoire, vous devez utiliser des structures partagées comme :

6.1. Valeur partagée (Value)

from multiprocessing import Value

def incremente(valeur):
    for _ in range(1000):
        valeur.value += 1

if __name__ == '__main__':
    v = Value('i', 0)  # 'i' pour entier
    p1 = multiprocessing.Process(target=incremente, args=(v,))
    p2 = multiprocessing.Process(target=incremente, args=(v,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print("Résultat final :", v.value)

6.2. Liste partagée (Array)

from multiprocessing import Array

def doubler(liste):
    for i in range(len(liste)):
        liste[i] *= 2

if __name__ == '__main__':
    arr = Array('i', [1, 2, 3, 4])
    p = multiprocessing.Process(target=doubler, args=(arr,))
    p.start()
    p.join()
    print(arr[:])

7. Communiquer avec des files (Queue)

Utilisez Queue pour envoyer des données d’un processus à un autre :

from multiprocessing import Queue

def producteur(q):
    for i in range(5):
        q.put(i)

def consommateur(q):
    while not q.empty():
        print("Reçu :", q.get())

if __name__ == '__main__':
    q = Queue()
    p1 = multiprocessing.Process(target=producteur, args=(q,))
    p2 = multiprocessing.Process(target=consommateur, args=(q,))
    p1.start()
    p1.join()
    p2.start()
    p2.join()

8. Synchroniser avec des verrous (Lock)

Pour éviter que plusieurs processus modifient une ressource en même temps, utilisez des verrous :

from multiprocessing import Lock

def affiche_message(lock, message):
    with lock:
        print(message)

if __name__ == '__main__':
    verrou = Lock()
    for i in range(3):
        p = multiprocessing.Process(target=affiche_message, args=(verrou, f"Message {i}"))
        p.start()

Exemple concret : traitement parallèle de données

def traitement(n):
    print(f"Traitement de {n}")
    return n ** 2

if __name__ == '__main__':
    with Pool(processes=4) as pool:
        donnees = list(range(10))
        resultats = pool.map(traitement, donnees)
        print("Résultats :", resultats)

10. Astuces et bonnes pratiques

  • Toujours protéger le point d’entrée avec if __name__ == '__main__'.
  • Évitez de partager trop de mémoire entre processus.
  • Privilégiez Pool pour les tâches similaires en masse.
  • Utilisez Queue ou Manager pour des échanges complexes.

Conclusion

Le module multiprocessing de Python est une solution élégante pour paralléliser vos programmes et tirer profit de tous les cœurs de votre CPU. Que ce soit pour accélérer des calculs, automatiser plusieurs tâches en simultané ou traiter de grandes quantités de données, cette bibliothèque vous permet de maîtriser le multitraitement en toute simplicité.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *