J'ai essayé de comprendre comment les threads fonctionnent en Python, et il est difficile de trouver de bonnes informations sur leur fonctionnement. Il me manque peut-être un lien ou quelque chose, mais il semble que la documentation officielle ne soit pas très complète sur le sujet, et je n'ai pas été en mesure de trouver une bonne rédaction.
D'après ce que je peux dire, un seul thread peut être exécuté à la fois et le thread actif change toutes les 10 instructions environ?
Où y a-t-il une bonne explication, ou pouvez-vous en fournir une? Il serait également très agréable d'être conscient des problèmes courants que vous rencontrez lors de l'utilisation de threads avec Python.
Python est un langage assez facile à enfiler, mais il y a des mises en garde. La plus grande chose que vous devez savoir est le verrouillage global de l'interprète. Cela permet à un seul thread d'accéder à l'interpréteur. Cela signifie deux choses: 1) vous vous retrouvez rarement à utiliser une instruction de verrouillage en python et 2) si vous voulez profiter des systèmes multiprocesseurs, vous devez utiliser des processus séparés. EDIT: Je dois également souligner que vous pouvez mettre une partie du code en C / C ++ si vous souhaitez également contourner le GIL.
Ainsi, vous devez reconsidérer pourquoi vous souhaitez utiliser des threads. Si vous souhaitez paralléliser votre application pour tirer parti de l'architecture double cœur, vous devez envisager de diviser votre application en plusieurs processus.
Si vous souhaitez améliorer la réactivité, vous devez CONSIDÉRER l'utilisation de threads. Il existe cependant d'autres alternatives, à savoir le micro-lecture . Il existe également quelques cadres que vous devriez examiner:
la source
Voici un exemple de filetage de base. Il engendrera 20 threads; chaque thread affichera son numéro de thread. Exécutez-le et observez l'ordre dans lequel ils s'impriment.
import threading class Foo (threading.Thread): def __init__(self,x): self.__x = x threading.Thread.__init__(self) def run (self): print str(self.__x) for x in xrange(20): Foo(x).start()
Comme vous l'avez indiqué, les threads Python sont implémentés par découpage temporel. C'est ainsi qu'ils obtiennent l'effet «parallèle».
Dans mon exemple, ma classe Foo étend le thread, j'implémente ensuite la
run
méthode, qui est l'endroit où va le code que vous souhaitez exécuter dans un thread. Pour démarrer le thread que vous appelezstart()
sur l'objet thread, qui appellera automatiquement larun
méthode ...Bien sûr, ce ne sont que les bases. Vous voudrez éventuellement en savoir plus sur les sémaphores, les mutex et les verrous pour la synchronisation des threads et le passage de messages.
la source
Utilisez des threads en python si les nœuds de calcul individuels effectuent des opérations liées aux E / S. Si vous essayez de mettre à l'échelle plusieurs cœurs sur une machine, recherchez un bon framework IPC pour python ou choisissez un autre langage.
la source
Remarque: partout où je mentionne,
thread
je veux dire spécifiquement les threads en python jusqu'à ce que cela soit explicitement indiqué.Les threads fonctionnent un peu différemment en python si vous venez de l'
C/C++
arrière-plan. En python, un seul thread peut être en état d'exécution à un moment donné, ce qui signifie que les threads en python ne peuvent pas vraiment tirer parti de la puissance de plusieurs cœurs de traitement car, de par leur conception, il n'est pas possible pour les threads de s'exécuter en parallèle sur plusieurs cœurs.Comme la gestion de la mémoire en python n'est pas sécurisée pour les threads, chaque thread nécessite un accès exclusif aux structures de données dans l'interpréteur python. Cet accès exclusif est acquis par un mécanisme appelé (verrou d'interprétation global) .
GIL
Why does python use GIL?
Afin d'empêcher plusieurs threads d'accéder simultanément à l'état de l'interpréteur et de corrompre l'état de l'interpréteur.
L'idée est qu'à chaque fois qu'un thread est en cours d'exécution (même s'il s'agit du thread principal) , un GIL est acquis et après un certain intervalle de temps prédéfini, le GIL est libéré par le thread actuel et réacquis par un autre thread (le cas échéant).
Why not simply remove GIL?
Ce n'est pas qu'il soit impossible de supprimer GIL, c'est juste qu'au cours de cette opération, nous finissons par mettre plusieurs verrous à l'intérieur de l'interpréteur afin de sérialiser l'accès, ce qui rend même une seule application threadée moins performante.
Ainsi, le coût de la suppression de GIL est payé par les performances réduites d'une seule application threadée, ce qui n'est jamais souhaité.
So when does thread switching occurs in python?
Le changement de thread se produit lorsque GIL est libéré, alors quand GIL est-il publié? Il y a deux scénarios à prendre en considération.
Si un thread effectue des opérations liées au processeur (traitement d'image Ex).
Dans les anciennes versions de python, le changement de thread se produisait après un nombre fixe d'instructions python.Il était par défaut défini sur
100
. peut très sauvagement de la milliseconde à même une seconde. Par conséquent, la libération de GIL après chaque100
instruction, quel que soit le temps qu'elles prennent pour s'exécuter, est une mauvaise politique.Dans les nouvelles versions, au lieu d'utiliser le nombre d'instructions comme métrique pour changer de thread, un intervalle de temps configurable est utilisé. L'intervalle de commutation par défaut est de 5 millisecondes. Vous pouvez obtenir l'intervalle de commutation actuel à l'aide de
sys.getswitchinterval()
. Cela peut être modifié en utilisantsys.setswitchinterval()
Si un thread effectue des opérations liées aux
E / S (accès au système de fichiers Ex ou E / S réseau)
GIL est libéré chaque fois que le thread attend que certaines opérations d'E / S soient terminées.
Which thread to switch to next?
L'interpréteur n'a pas son propre ordonnanceur, quel thread est planifié à la fin de l'intervalle est la décision du système d'exploitation. .
la source
Une solution simple au GIL est le module multitraitement . Il peut être utilisé en remplacement du module de threading mais utilise plusieurs processus Interpreter au lieu de threads. Pour cette raison, il y a un peu plus de frais généraux que le threading ordinaire pour des choses simples, mais cela vous donne l'avantage d'une réelle parallélisation si vous en avez besoin. Il s'adapte également facilement à plusieurs machines physiques.
Si vous avez besoin d'une parallélisation à grande échelle, je chercherais plus loin, mais si vous voulez simplement mettre à l'échelle tous les cœurs d'un ordinateur ou quelques différents sans tout le travail nécessaire à la mise en œuvre d'un cadre plus complet, c'est pour vous. .
la source
Essayez de vous rappeler que le GIL est configuré pour interroger de temps en temps afin de montrer l'apparence de plusieurs tâches. Ce paramètre peut être affiné, mais je suggère qu'il devrait y avoir du travail que les threads font ou que de nombreux changements de contexte vont causer des problèmes.
J'irais jusqu'à suggérer plusieurs parents sur les processeurs et essayer de garder les mêmes emplois sur le (s) même (s) cœur (s).
la source