2009-11-08 6 views
11

J'essaie d'utiliser des threads dans un projet Python sur lequel je travaille, mais les threads ne semblent pas se comporter comme ils le devraient dans mon code. Il semble que tous les threads s'exécutent séquentiellement (c'est-à-dire que thread2 démarre après la fin du thread 1, ils ne démarrent pas tous les deux en même temps). J'ai écrit un script simple pour le tester, et cela aussi exécute des threads de manière séquentielle.Le thread Python semble exécuter des threads de façon séquentielle

import threading 

def something(): 
    for i in xrange(10): 
     print "Hello" 

def my_thing(): 
    for i in xrange(10): 
     print "world" 

threading.Thread(target=something).start() 
threading.Thread(target=my_thing).start() 

est ici la sortie que je reçois de l'exécuter:

Hello 
Hello 
Hello 
Hello 
Hello 
Hello 
Hello 
Hello 
Hello 
Hello 
world 
world 
world 
world 
world 
world 
world 
world 
world 
world 

Le même comportement est observé avec beaucoup plus grand nombre d'itérations des boucles.

J'ai essayé de chercher sur le Web et les anciennes réponses SO, mais je n'ai rien trouvé qui m'aide. Quelqu'un peut-il signaler ce qui ne va pas avec ce code?

Répondre

13

Actuellement en mode python, les unités d'exécution sont modifiées après l'exécution d'une certaine quantité d'instructions de type bytecode. Ils ne courent pas en même temps. Vous ne disposerez que de threads s'exécutant en parallèle lorsque l'un d'entre eux appelle un module intensif d'E/S ou non python qui peut libérer GIL (verrou global de l'interpréteur).

Je suis sûr que vous obtiendrez la sortie mélangée si vous cognez le nombre de boucles à quelque chose comme 10000. Rappelez-vous que simplement engendrer le deuxième thread prend aussi beaucoup de temps.

+0

Même comportement avec 10000 itérations – MAK

+0

Sur le projet sur lequel je travaille, l'un des threads est une boucle infinie qui écoute les messages et appelle une fonction de rappel comme ils arrivent. Il bloque juste tous les autres threads. Malheureusement, le code de boucle actuel ne peut pas être modifié (j'appelle simplement la méthode run() d'une classe dans le thread). – MAK

+0

Lorsque je lance le script comme ceci: './pythr.py | uniq -c' je reçois: 8969 Bonjour | 1 Bonjour tout le monde | Monde 6626 | 1 | 3373 Monde | 1030 Bonjour. Cela change donc le contrôle - mais pas si souvent ... – viraptor

10

Pendant le temps nécessaire au second thread pour démarrer le premier thread, il exécute une boucle et imprime déjà.

Ici, il ressemble à ceci, vous pouvez voir le 2ème fil commençant après le premier a émis quelques hellos.

Hello 
Hello 
Hello 
Hello 
Hello 
Helloworld 

Helloworld 

Helloworld 

Helloworld 

Helloworld 

world 
world 
world 
world 
world 

Btw: Votre exemple n'est pas significatif du tout. La seule raison pour Threads est IO, et IO est lent. Lorsque vous ajoutez un certain sommeil pour simuler IO il devrait fonctionner comme prévu:

import threading 
from time import sleep 

def something(): 
    for i in xrange(10): 
     sleep(0.01) 
     print "Hello" 

def my_thing(): 
    for i in xrange(10): 
     sleep(0.01) 
     print "world" 

threading.Thread(target=something).start() 
threading.Thread(target=my_thing).start() 

un mélange sauvage apparaît:

worldHello 

Helloworld 

Helloworld 

worldHello 

Helloworld 

Helloworld 

worldHello 

Helloworld 

worldHello 

Helloworld 
+2

Je ne reçois pas de sortie comme cela même avec un nombre beaucoup plus grand/plus petit d'itérations des boucles for. Sur mon ordinateur, c'est toujours séquentiel. Je pense que cela dépend du système d'exploitation/du processeur, comme l'a suggéré abyx. – MAK

+0

Comme je l'ai dit dans ma question, ce n'est qu'un exemple pour mon problème, pas le code avec lequel je travaille (qui est beaucoup plus gros). Dans mon code actuel, l'un des threads exécute une boucle d'écoute pour les signaux dbus. – MAK

3

Cela dépend vraiment de votre planificateur de système d'exploitation, votre processeur. A part cela, on sait que les threads de CPython ne sont pas parfaits à cause du GIL (PDF), ce qui signifie que beaucoup de fois les threads fonctionnent séquentiellement, ou quelque chose de ce genre.

+2

Vous voulez probablement dire que les threads CPython souffrent du GIL ... Il n'y a pas de GIL, par exemple, Jython. – EOL

+0

@EOL - vous avez raison, j'ai mis à jour la réponse – abyx

4

Le comportement peut également changer selon que le système utilise un ou plusieurs processeurs, comme expliqué par this talk par David Beazley. Comme le dit viraptor, le premier thread libérera le GIL après l'exécution de sys.getcheckinterval() bytecodes (100 par défaut). Pour résumer crûment ce que David Beazley dit, sur un système à processeur unique, le deuxième fil aura alors une chance de prendre le relais. Cependant, sur un système multicœur, le deuxième thread peut fonctionner sur un noyau différent, et le premier thread essayera de réacquérir le verrou et réussira probablement puisque le système d'exploitation n'aura pas eu le temps de changer de processeur. Cela signifie que sur un système multi-core avec un thread lié à la CPU, les autres threads peuvent ne jamais regarder.

La méthode consiste à ajouter une instruction sleep aux deux boucles afin qu'elles ne soient plus visibles. CPU lié.

Questions connexes