web-dev-qa-db-fra.com

Est-ce que Python supporte le multithreading? Peut-il accélérer le temps d'exécution?

Je suis un peu confus quant à savoir si le multithreading fonctionne dans Python ou non.

Je sais qu'il y a eu beaucoup de questions à ce sujet et j'en ai lu beaucoup, mais je suis toujours confus. Je sais par expérience et j'ai vu d'autres personnes poster leurs propres réponses et exemples ici sur StackOverflow que le multithreading est effectivement possible en Python. Alors pourquoi est-ce que tout le monde continue à dire que Python est verrouillé par le GIL et qu’un seul thread peut fonctionner à la fois? Cela fonctionne clairement. Ou y at-il une distinction que je n’obtiens pas? ici?

De nombreuses affiches/répondants continuent également de mentionner que le filetage est limité car il ne fait pas appel à plusieurs cœurs. Mais je dirais qu'ils sont toujours utiles car ils travaillent simultanément et permettent donc de faire la charge de travail combinée plus rapidement. Je veux dire pourquoi il y aurait même un module de thread Python autrement?

Mise à jour:

Merci pour toutes les réponses jusqu'à présent. Si j'ai bien compris, le multithreading ne fonctionnera en parallèle que pour certaines tâches IO), mais ne peut être exécuté qu'une à la fois pour plusieurs tâches principales liées au processeur.

Je ne suis pas tout à fait sûr de ce que cela signifie concrètement pour moi. Je vais donc donner un exemple du type de tâche que je voudrais multithread. Par exemple, supposons que je souhaite parcourir une très longue liste de chaînes et effectuer quelques opérations de base sur chaque élément de la liste. Si je scinde la liste, envoie chaque sous-liste à traiter avec mon code de boucle/chaîne dans un nouveau thread, et renvoie les résultats dans une file d'attente, ces charges de travail s'exécuteront-elles à peu près au même moment? Plus important encore, cela va-t-il théoriquement accélérer le temps nécessaire à l'exécution du script?

Un autre exemple pourrait être si je peux restituer et enregistrer quatre images différentes à l'aide de PIL dans quatre threads différents, et que cela soit plus rapide que de traiter les images une par une après l'autre. Je suppose que cette composante de vitesse est ce à quoi je m'interroge vraiment plutôt que de savoir quelle est la terminologie correcte.

Je connais aussi le module de multitraitement, mais mon principal intérêt pour le moment est de charger des tâches de petites à moyennes (10 à 30 secondes) et je pense donc que le multithreading sera plus approprié car les sous-processus peuvent être lents à démarrer.

79
Karim Bahgat

Le GIL n'empêche pas le filetage. Tout ce que GIL fait, c'est de s'assurer qu'un seul thread exécute Python code à la fois; contrôle toujours bascule entre les threads.

Ce que GIL empêche alors, c’est d’utiliser plus d’un cœur de processeur ou des processeurs distincts pour exécuter des threads en parallèle.

Ceci s'applique uniquement au code Python. Les extensions C peuvent et libèrent la GIL pour autoriser plusieurs threads de code C et un thread Python à s'exécuter sur plusieurs cœurs. Cela s'étend aux E/S contrôlées par le noyau, telles que select() appelle les lectures et écritures de socket, ce qui permet à Python de gérer les événements réseau de manière raisonnablement efficace dans une configuration multicœur multi-thread.

De nombreux déploiements de serveurs exécutent ensuite plusieurs processus Python, afin de permettre au système d'exploitation de gérer la planification entre les processus pour utiliser au maximum les cœurs de votre processeur. Vous pouvez également utiliser la bibliothèque multiprocessing library pour gérer le traitement parallèle de plusieurs processus à partir d’une base de code et d’un processus parent, si cela convient à vos cas d’utilisation.

Notez que la GIL ne s'applique qu'à l'implémentation CPython; Jython et IronPython utilisent une implémentation de thread différente (les threads d'exécution communs natifs Java VM et .NET, respectivement).

Pour adresser directement votre mise à jour: toute tâche qui tente d'obtenir une accélération de la vitesse d'exécution parallèle, à l'aide de code pur Python, ne verra pas d'accélération car le code threadé Python est verrouillé sur un thread s'exécutant à la fois. Si vous mélangez des extensions C et des E/S (toutefois, comme les opérations PIL ou numpy), tout code C peut être exécuté en parallèle avec le thread one actif Python.

Les threads Python sont parfaits pour créer une interface utilisateur graphique réactive ou pour gérer plusieurs demandes Web courtes dans lesquelles l'entrée/sortie est le goulot d'étranglement plus que le code Python. Il ne convient pas pour la parallélisation de code Python de calcul intensif, tenez-vous-en au module multiprocessing pour ces tâches ou déléguez-le à une bibliothèque externe dédiée.

107
Martijn Pieters

Oui. :)

Vous avez le niveau bas thread module et le niveau supérieur threading module. Mais si vous voulez simplement utiliser des machines multicœurs, le module multitraitement est la solution.

Citation de la docs :

Dans CPython, en raison du verrou d'interprète global, un seul thread peut exécuter le code Python à la fois (même si certaines bibliothèques orientées sur les performances peuvent dépasser cette limitation). Si vous voulez que votre application Pour utiliser les ressources de calcul des machines multicœurs, il est conseillé d’utiliser le multitraitement.Toutefois, le threading reste un modèle approprié si vous souhaitez exécuter simultanément plusieurs tâches liées aux E/S.

3
zord

Les threads sont autorisés en Python, le seul problème est que GIL s'assurera qu'un seul thread est exécuté à la fois (pas de parallélisme).

Donc, si vous voulez multi-threader le code pour accélérer le calcul, il ne l’accélérera pas car un seul thread est exécuté à la fois, mais si vous l’utilisez pour interagir avec une base de données, par exemple.

1
r.guerbab

Python 3 a la facilité de Lancer des tâches parallèles . Cela facilite notre travail.

Il a pour pool de threads et pool de processus .

Ce qui suit donne un aperçu:

Exemple de ThreadPoolExecutor

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

ProcessPoolExecutor

import concurrent.futures
import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419]

def is_prime(n):
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for number, prime in Zip(PRIMES, executor.map(is_prime, PRIMES)):
            print('%d is prime: %s' % (number, prime))

if __== '__main__':
    main()
0
Jeril