Sobes.tech
Junior

Qu'est-ce que le parallélisme en programmation?

sobes.tech IA

Réponse de l'IA

Le parallélisme en programmation est l'exécution de plusieurs tâches simultanément ou l'apparence d'une exécution simultanée, ce qui permet d'améliorer la performance et la réactivité des applications.

On distingue plusieurs types de parallélisme :

  • Multiprocessing : Utilise plusieurs processus indépendants, chacun avec son propre espace d'adressage. Cela contourne la limitation du GIL (Global Interpreter Lock) dans CPython et convient aux tâches intensives en CPU.

    import multiprocessing
    import time
    
    def worker(num):
        """Un certain travail"""
        time.sleep(1)
        print(f'Worker {num} terminé')
    
    if __name__ == '__main__':
        processes = []
        for i in range(5):
            p = multiprocessing.Process(target=worker, args=(i,))
            processes.append(p)
            p.start()
    
        for p in processes:
            p.join()
        print("Tous les workers ont terminé")
    
  • Multithreading : Utilise plusieurs threads au sein d'un même processus, partageant le même espace d'adressage. En raison du GIL dans CPython, les threads ne s'exécutent pas en parallèle sur différents cœurs pour les tâches CPU-bound, mais sont efficaces pour les tâches I/O-bound.

    import threading
    import time
    
    def worker(num):
        """Un certain travail"""
        time.sleep(1) # Simulation d'une opération I/O
        print(f'Thread {num} terminé')
    
    threads = []
    for i in range(5):
        t = threading.Thread(target=worker, args=(i,))
        threads.append(t)
        t.start()
    
    for t in threads:
        t.join()
    print("Tous les threads ont terminé")
    
  • Programmation asynchrone (Asyncio) : Utilise un seul thread et une boucle d'événements (event loop) pour gérer l'exécution des tâches. Convient pour les applications I/O-bound à haute charge et les serveurs web. Ne bloque pas l'exécution en attendant l'I/O.

    import asyncio
    import time
    
    async def worker(num):
        """Un certain travail asynchrone"""
        await asyncio.sleep(1) # Attente asynchrone d'I/O
        print(f'Worker asynchrone {num} terminé')
    
    async def main():
        tasks = []
        for i in range(5):
            task = asyncio.create_task(worker(i))
            tasks.append(task)
        await asyncio.gather(*tasks)
        print("Tous les workers asynchrones ont terminé")
    
    if __name__ == '__main__':
        asyncio.run(main())
    

Le choix de l'approche dépend de la nature de la tâche :

Approche Tâches CPU-bound Tâches I/O-bound GIL Utilisation de la mémoire
Multiprocessing Excellent Bon Le contourne Grande (son propre processus)
Multithreading Mauvais (à cause du GIL) Excellent Bloque Petite (processus partagé)
Asynchrone Mauvais Excellent Ne bloque pas Petite (un seul thread)

Le parallélisme nécessite une gestion prudente de la synchronisation et des ressources pour éviter les conditions de course et les blocages mutuels.