web-dev-qa-db-fra.com

Python threads exécutant tous sur un seul noyau

J'ai un programme Python qui génère de nombreux threads, exécute 4 à la fois, et chacun effectue une opération coûteuse. Pseudocode:

for object in list:
    t = Thread(target=process, args=(object))
    # if fewer than 4 threads are currently running, t.start(). Otherwise, add t to queue

Mais lorsque le programme est exécuté, Activity Monitor sous OS X montre que 1 des 4 cœurs logiques est à 100% et les autres à près de 0. Évidemment, je ne peux pas forcer le système d'exploitation à faire quoi que ce soit, mais je n'ai jamais eu à le faire faites attention aux performances dans le code multi-thread comme celui-ci auparavant, donc je me demandais si je manquais ou malaisais quelque chose.

Merci.

52
Rob Lourens

Notez que dans de nombreux cas (et pratiquement tous les cas où votre "opération coûteuse" est un calcul implémenté en Python), plusieurs threads ne s'exécuteront pas simultanément en raison de Python Global Interpreter Lock (GIL) .

Le GIL est un verrou de niveau interprète. Ce verrou empêche l'exécution de plusieurs threads à la fois dans l'interpréteur Python. Chaque thread qui veut s'exécuter doit attendre que le GIL soit libéré par l'autre thread, ce qui signifie que votre application Python à plusieurs threads est essentiellement à thread unique, non? Oui. Pas exactement. Sorte de.

CPython utilise ce que l'on appelle des threads de "système d'exploitation" sous les couvertures, c'est-à-dire que chaque fois qu'une demande de création d'un nouveau thread est effectuée, l'interpréteur appelle en fait les bibliothèques et le noyau du système d'exploitation pour générer un nouveau thread. C'est la même chose que Java, par exemple. Donc, en mémoire, vous avez vraiment plusieurs threads et normalement le système d'exploitation contrôle quel thread est prévu pour s'exécuter. Sur une machine à plusieurs processeurs, cela signifie que vous pouvez avoir de nombreux threads répartis sur plusieurs processeurs, tous heureusement en train de travailler.

Cependant, alors que CPython utilise des threads du système d'exploitation (en théorie, permettant à plusieurs threads de s'exécuter simultanément dans l'interpréteur), l'interpréteur force également le GIL à être acquis par un thread avant de pouvoir accéder à l'interpréteur et à la pile et peut modifier Python objets en mémoire tous bon gré mal gré. Ce dernier point est la raison pour laquelle le GIL existe: Le GIL empêche l'accès simultané aux objets Python par plusieurs threads. Mais cela ne vous empêche pas (comme illustré par l'exemple de la Banque) d'être une créature sensible au verrouillage; vous ne bénéficiez pas d'un trajet gratuit. Le GIL est là pour protéger la mémoire des interprètes, pas votre santé mentale.

Voir la section Global Interpreter Lock de Post de Jesse Noller pour plus de détails.

Pour contourner ce problème, consultez module multiprocessing de Python .

les processus multiples (avec une utilisation judicieuse de l'IPC) sont [...] une bien meilleure approche pour écrire des applications pour des boîtiers multi-CPU que des threads.

- Guido van Rossum (créateur de Python)

68
Gabriel Grant

Python dispose d'un verrou d'interpréteur global, qui peut empêcher le traitement simultané des threads de code interprété.

http://en.wikipedia.org/wiki/Global_Interpreter_Lock

http://wiki.python.org/moin/GlobalInterpreterLock

Pour savoir comment contourner ce problème, essayez le module multiprocessing , comme indiqué ici:

L'exécution de processus python évite-t-elle le GIL?

9
T.R.

AFAIK, dans CPython, le Global Interpreter Lock signifie qu'il ne peut pas y avoir plus d'un bloc de code Python en cours d'exécution à la fois. Bien que cela n'affecte vraiment rien dans un seul processeur/machine monocœur, sur une machine mulitcore, cela signifie que vous n'avez effectivement qu'un seul thread en cours d'exécution à la fois - ce qui fait que tous les autres cœurs sont inactifs.

2
MAK