Maison >développement back-end >Tutoriel Python >Comprendre le threading et le multitraitement en Python : un guide complet

Comprendre le threading et le multitraitement en Python : un guide complet

PHPz
PHPzoriginal
2024-09-12 14:17:02593parcourir

Understanding Threading and Multiprocessing in Python: A Comprehensive Guide

Introduction

En Python, les concepts de threading et de multitraitement sont souvent abordés lors de l'optimisation des performances des applications, en particulier lorsqu'elles impliquent une exécution simultanée ou parallèle. Malgré le chevauchement des terminologies, ces deux approches sont fondamentalement différentes.

Ce blog aidera à clarifier la confusion autour du threading et du multitraitement, expliquera quand utiliser chacun et fournira des exemples pertinents pour chaque concept.


Threading vs multitraitement : différences clés

Avant de plonger dans des exemples et des cas d'utilisation, décrivons les principales différences :

  • Threading : fait référence à l'exécution de plusieurs threads (unités plus petites d'un processus) au sein d'un seul processus. Les threads partagent le même espace mémoire, ce qui les rend légers. Cependant, le Global Interpreter Lock (GIL) de Python limite le véritable parallélisme du threading pour les tâches liées au processeur.

  • Multitraitement : implique l'exécution de plusieurs processus, chacun avec son propre espace mémoire. Les processus sont plus lourds que les threads mais peuvent atteindre un véritable parallélisme car ils ne partagent pas de mémoire. Cette approche est idéale pour les tâches liées au processeur où une utilisation complète du cœur est nécessaire.


Qu'est-ce que le Threading ?

Le

Threading est un moyen d'exécuter plusieurs tâches simultanément au sein du même processus. Ces tâches sont gérées par des threads, qui sont des unités d'exécution distinctes et légères partageant le même espace mémoire. Le threading est bénéfique pour les opérations liées aux E/S, telles que la lecture de fichiers, les requêtes réseau ou les requêtes de base de données, où le programme principal passe beaucoup de temps à attendre des ressources externes.

Quand utiliser le threading

  • Lorsque votre programme est lié aux E/S (par exemple, lire/écrire des fichiers, effectuer des requêtes réseau).
  • Lorsque les tâches passent beaucoup de temps à attendre des opérations d'entrée ou de sortie.
  • Lorsque vous avez besoin d'une concurrence légère au sein d'un seul processus.

Exemple : Threading de base

import threading
import time

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

def print_letters():
    for letter in ['a', 'b', 'c', 'd', 'e']:
        print(letter)
        time.sleep(1)

# Create two threads
t1 = threading.Thread(target=print_numbers)
t2 = threading.Thread(target=print_letters)

# Start both threads
t1.start()
t2.start()

# Wait for both threads to complete
t1.join()
t2.join()

print("Both threads finished execution.")

Dans l'exemple ci-dessus, deux threads s'exécutent simultanément : l'un imprime des chiffres et l'autre imprime des lettres. Les appels sleep() simulent des opérations d'E/S et le programme peut basculer entre les threads pendant ces attentes.

Le problème avec le threading : le verrouillage global de l'interprète (GIL)

Le GIL de Python est un mécanisme qui empêche plusieurs threads natifs d'exécuter simultanément des bytecodes Python. Cela garantit qu'un seul thread s'exécute à la fois, même si plusieurs threads sont actifs dans le processus.

Cette limitation rend le threading inadapté aux tâches liées au processeur qui nécessitent un réel parallélisme, car les threads ne peuvent pas utiliser pleinement plusieurs cœurs en raison du GIL.


Qu'est-ce que le multitraitement ?

Le

Multitraitement vous permet d'exécuter plusieurs processus simultanément, où chaque processus possède son propre espace mémoire. Étant donné que les processus ne partagent pas de mémoire, il n'y a aucune restriction GIL, permettant une véritable exécution parallèle sur plusieurs cœurs de processeur. Le multitraitement est idéal pour les tâches liées au processeur qui doivent maximiser l'utilisation du processeur.

Quand utiliser le multitraitement

  • Lorsque votre programme est lié au processeur (par exemple, lors de calculs lourds, de traitement de données).
  • Quand vous avez besoin d'un véritable parallélisme sans partage de mémoire.
  • Lorsque vous souhaitez exécuter plusieurs instances d'une tâche indépendante simultanément.

Exemple : Multitraitement de base

import multiprocessing
import time

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

def print_letters():
    for letter in ['a', 'b', 'c', 'd', 'e']:
        print(letter)
        time.sleep(1)

if __name__ == "__main__":
    # Create two processes
    p1 = multiprocessing.Process(target=print_numbers)
    p2 = multiprocessing.Process(target=print_letters)

    # Start both processes
    p1.start()
    p2.start()

    # Wait for both processes to complete
    p1.join()
    p2.join()

    print("Both processes finished execution.")

Dans cet exemple, deux processus distincts s'exécutent simultanément. Contrairement aux threads, chaque processus possède son propre espace mémoire et s'exécute indépendamment sans interférence du GIL.

Isolement de la mémoire en multitraitement

Une différence clé entre le threading et le multitraitement est que les processus ne partagent pas de mémoire. Bien que cela garantisse qu'il n'y a aucune interférence entre les processus, cela signifie également que le partage de données entre eux nécessite des mécanismes spéciaux, tels que des objets Queue, Pipe ou Manager fournis par le module multitraitement.


Threading vs multitraitement : choisir le bon outil

Maintenant que nous comprenons comment fonctionnent les deux approches, voyons quand choisir le threading ou le multitraitement en fonction du type de tâches :

Use Case Type Why?
Network requests, I/O-bound tasks (file read/write, DB calls) Threading Multiple threads can handle I/O waits concurrently.
CPU-bound tasks (data processing, calculations) Multiprocessing True parallelism is possible by utilizing multiple cores.
Task requires shared memory or lightweight concurrency Threading Threads share memory and are cheaper in terms of resources.
Independent tasks needing complete isolation (e.g., separate processes) Multiprocessing Processes have isolated memory, making them safer for independent tasks.

Performance Considerations

Threading Performance

Threading excels in scenarios where the program waits on external resources (disk I/O, network). Since threads can work concurrently during these wait times, threading can help boost performance.

However, due to the GIL, CPU-bound tasks do not benefit much from threading because only one thread can execute at a time.

Multiprocessing Performance

Multiprocessing allows true parallelism by running multiple processes across different CPU cores. Each process runs in its own memory space, bypassing the GIL and making it ideal for CPU-bound tasks.

However, creating processes is more resource-intensive than creating threads, and inter-process communication can slow things down if there's a lot of data sharing between processes.


A Practical Example: Threading vs. Multiprocessing for CPU-bound Tasks

Let's compare threading and multiprocessing for a CPU-bound task like calculating the sum of squares for a large list.

Threading Example for CPU-bound Task

import threading

def calculate_squares(numbers):
    result = sum([n * n for n in numbers])
    print(result)

numbers = range(1, 10000000)
t1 = threading.Thread(target=calculate_squares, args=(numbers,))
t2 = threading.Thread(target=calculate_squares, args=(numbers,))

t1.start()
t2.start()

t1.join()
t2.join()

Due to the GIL, this example will not see significant performance improvements over a single-threaded version because the threads can't run simultaneously for CPU-bound operations.

Multiprocessing Example for CPU-bound Task

import multiprocessing

def calculate_squares(numbers):
    result = sum([n * n for n in numbers])
    print(result)

if __name__ == "__main__":
    numbers = range(1, 10000000)
    p1 = multiprocessing.Process(target=calculate_squares, args=(numbers,))
    p2 = multiprocessing.Process(target=calculate_squares, args=(numbers,))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

In the multiprocessing example, you'll notice a performance boost since both processes run in parallel across different CPU cores, fully utilizing the machine's computational resources.


Conclusion

Understanding the difference between threading and multiprocessing is crucial for writing efficient Python programs. Here’s a quick recap:

  • Use threading for I/O-bound tasks where your program spends a lot of time waiting for resources.
  • Use multiprocessing for CPU-bound tasks to maximize performance through parallel execution.

Knowing when to use which approach can lead to significant performance improvements and efficient use of resources.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn