2009-11-08 8 views
11

Sto cercando di utilizzare i thread in un progetto Python su cui sto lavorando, ma i thread non sembrano comportarsi come dovrebbero nel mio codice. Sembra che tutti i thread vengano eseguiti in modo sequenziale (cioè thread2 inizia dopo che il thread 1 termina, entrambi non iniziano contemporaneamente). Ho scritto un semplice script per testarlo e anche questo esegue i thread in modo sequenziale.Sembra che il threading Python esegua i thread in sequenza

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() 

Ecco l'output che ricevo da eseguirlo:

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

Lo stesso comportamento viene osservato con molto più grande numero di iterazioni dei cicli.

Ho provato a cercare sul Web e le risposte SO meno recenti, ma non sono riuscito a trovare nulla che mi aiutasse. Qualcuno può indicare cosa c'è di sbagliato in questo codice?

risposta

13

Attualmente in python, i thread vengono modificati dopo l'esecuzione di alcune quantità di istruzioni bytecode. Non corrono allo stesso tempo. Avrai thread in esecuzione solo in parallelo quando uno di loro chiama un modulo I/O-intensive o non Python che può rilasciare GIL (global interpreter lock).

Sono quasi certo che l'output verrà mescolato se si aumenta il numero di loop a qualcosa come 10000. Ricordare che la semplice generazione del secondo thread richiede anche "molto" tempo.

+0

Stesso comportamento con 10000 iterazioni – MAK

+0

Sul progetto attuale su cui sto lavorando, uno dei thread è un ciclo infinito che ascolta i messaggi e chiama una funzione di callback come arrivano. Blocca solo tutti gli altri thread. Sfortunatamente, il codice loop attuale non può essere modificato (ho appena chiamato il metodo run() di una classe all'interno del thread). – MAK

+0

Quando eseguo lo script in questo modo: './pythr.py | uniq -c' Ottengo: 8969 Ciao | 1 Ciao mondo | 6626 mondo | 1 | 3373 mondo | 1030 Ciao. Quindi cambia il controllo - non è così spesso ... – viraptor

10

Nel tempo necessario al secondo thread per avviare i primi cicli di filo e stampare già.

Qui appare così, è possibile vedere il secondo thread che inizia dopo il primo emesso qualche hellos.

Hello 
Hello 
Hello 
Hello 
Hello 
Helloworld 

Helloworld 

Helloworld 

Helloworld 

Helloworld 

world 
world 
world 
world 
world 

A proposito: il tuo esempio non è affatto significativo. L'unico motivo per Thread è IO e IO è lento. Quando si aggiunge un po 'di sonno per simulare IO dovrebbe funzionare come previsto: appare

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 mix selvaggio:

worldHello 

Helloworld 

Helloworld 

worldHello 

Helloworld 

Helloworld 

worldHello 

Helloworld 

worldHello 

Helloworld 
+2

Non ottengo l'output in questo modo anche con un numero molto più grande/più piccolo di iterazioni dei cicli for. Sul mio computer è sempre sequenziale. Penso che questo dipenda da SO/processore, come suggerito da abyx. – MAK

+0

Come ho detto nella mia domanda, questo è solo un esempio per il mio problema, non il codice con cui sto lavorando (che è molto più grande). Nel mio codice attuale, uno dei thread esegue un ciclo di ascolto dei segnali dbus. – MAK

3

Questo in realtà dipende scheduler del sistema operativo, il processore.
Oltre a ciò, è noto che i thread di CPython non sono perfetti a causa dello GIL (PDF), che, in breve, significa che un sacco di volte i thread vengono eseguiti in sequenza o qualcosa del genere.

+2

Probabilmente vuoi dire che i thread CPython soffrono del GIL ... Non c'è GIL in, per esempio, Jython. – EOL

+0

@EOL - sei corretto, ho aggiornato la risposta – abyx

4

Il comportamento può anche variare a seconda che il sistema stia utilizzando un processore singolo o più processori, come spiegato da David Beazley this talk.

Come dice viraptor, il primo thread rilascia il GIL dopo l'esecuzione di sys.getcheckinterval() bytecodes (100 per impostazione predefinita). Per riassumere sommariamente ciò che dice David Beazley, su un singolo processore il secondo thread avrà quindi la possibilità di prendere il sopravvento. Tuttavia su un sistema multi-core il secondo thread può essere eseguito su un core differente, e il primo thread proverà a riacquisire il lock e probabilmente avrà successo poiché il sistema operativo non avrà avuto il tempo di cambiare processore. Ciò significa che su un sistema multi-core con un thread legato alla CPU gli altri thread potrebbero non dare mai un'occhiata.

Il modo per aggirare questo è aggiungere un'istruzione di sonno a entrambi i loop in modo che non siano più CPU vincolata.