2012-04-03 17 views
5

il codice di prova è molto semplice:Come funziona coda in python

import threading, Queue 
import time, random 

class Worker(threading.Thread): 
    def __init__(self, index, queue): 
     threading.Thread.__init__(self) 
     self.index = index 
     self.queue = queue 
    def run(self): 
     while 1: 
      time.sleep(random.random()) 
      item = self.queue.get() 
      if item is None: 
       break 
      print "index:", self.index, "task", item, "finished" 
      self.queue.task_done() 

queue = Queue.Queue(0) 
for i in range(2): 
    Worker(i, queue).start() 
for i in range(10): 
    queue.put(i) 
for i in range(2): 
    queue.put(None) 
print "Main OK" 

e il risultato è un po 'ogni volta diverso l'eseguo, qui è solo una:

Main OK 
index: 1 task 0 finished 
index: 0 task 1 finished 
index: 0 task 2 finished 
index: 1 task 3 finished 
index: 1 task 4 finished 
index: 0 task 5 finished 
index: 1 task 6 finished 
index: 0 task 7 finished 
index: 1 task 8 finished 
index: 1 task 9 finished 

IMO quando il thread principale è terminato, verrà stampato "Main OK", quindi il primo thread verrà eseguito fino a quando non sarà disponibile in time.sleep(random.random()), quindi il primo thread verrà interrotto e il secondo thread continuerà. uguale al primo thread, il secondo thread si addormenta quando viene eseguito in time.sleep(random.random()), quindi il primo thread continuerà di nuovo. e stamperà index:0 task 0 finished subito dopo Main OK, ma in realtà ciò che segue Main OK è index: 1... non index: 0...! perché? e sembra che la coda non funzioni come un multi-thread normale, a volte lo stesso thread di indice viene eseguito tre volte o più continuamente! In che mondo funziona il meccanismo Queue? ogni aiuto è apprezzato!

+0

Si prega di non fare multi-threading se non capisco. –

risposta

2

Hai tre thread; due thread Worker e un thread principale. Tutti e tre stanno funzionando allo stesso tempo, virtualmente. Quello che tu suggerisci (quell'interruttore chiaro in punti che conoscerai in anticipo) non è vero. Si avviano i thread Worker prima che la coda sia riempita, quindi iniziano a correre nella loro sleep() in una volta. Quindi si riempie la coda. Molto probabilmente uno dei thread lascerà il suo sleep() prima dell'altro e otterrà il primo elemento dalla coda, lo elaborerà (stampa) e passerà nuovamente al successivo sleep(). È possibile (a caso) che il primo lavoratore dorma 0,01 ogni volta mentre l'altro sta dormendo per 0,4 secondi dall'inizio, quindi tutti gli elementi saranno elaborati dal primo processo.

Se più di un thread di lavoro sta bloccando nel metodo Queue.get() (può accadere solo se entrambi hanno lasciato il proprio sleep() mentre la coda non è ancora stata compilata), non si ha alcun determinismo su quale thread di lavoro è svegliato per elaborare l'oggetto.

Il tuo sonno casuale non si sincronizza abbastanza da avere un chiaro passaggio tra i due lavoratori perché uno potrebbe dormire così a lungo con quel sonno casuale che l'altro thread elabora due elementi nello stesso tempo. Prova questo per un passaggio chiaro tra i due processi tutto il tempo:

def run(self): 
    if self.index == 0: 
     time.sleep(0.1) 
    while 1: 
     time.sleep(0.2) 
     item = self.queue.get() 
     if item is None: 
      break 
     print "index:", self.index, "task", item, "finished" 
     self.queue.task_done() 
+0

Ma ovviamente la sincronizzazione usando sleep() è una cattiva idea. – Alfe

4

Non ci sono garanzie su quale ordine verranno eseguiti i thread. Se ci fosse una garanzia dell'ordine, comporterebbe una forte sincronizzazione e quindi sarebbe equivalente alle prestazioni serializzate.

Inoltre, è necessario utilizzare queue.join() per attendere (bloccare) il completamento di tutti i lavoratori.

2

Mentre è vero che l'ordine di esecuzione del thread non è garantito, è disponibile anche uno time.sleep(random.random()).

>>> random.random() 
0.044693605707810558 
>>> random.random() 
0.16270424255105465 
>>> random.random() 
0.74068552817650446