2012-02-08 9 views
6

Próbuję skonfigurować prosty system producent-konsumentów Gevent ale mój skrypt nie exit:Gevent wątki nie dokończyć chociaż wszystkie elementy kolejki są wyczerpane

import gevent 
from gevent.queue import * 
import time 
import random 

q = Queue() 
workers = [] 

def do_work(wid, value): 
    """ 
    Actual blocking function 
    """ 
    gevent.sleep(random.randint(0,2)) 
    print 'Task', value, 'done', wid 
    return 


def worker(wid): 
    """ 
    Consumer 
    """ 
    while True: 
     item = q.get() 
     do_work(wid, item) 


def producer(): 
    """ 
    Producer 
    """ 
    for i in range(4): 
     workers.append(gevent.spawn(worker, random.randint(1, 100000))) 


    for item in range(1, 9): 
     q.put(item) 

producer() 
gevent.joinall(workers) 

I haven” Byliśmy w stanie znaleźć dobre przykłady/tutoriale na temat korzystania z Gevent, więc to, co wkleiłem powyżej, jest tym, co stworzyłem z Internetu.

Aktywuje się wielu pracowników, elementy są przenoszone do kolejki, ale nawet gdy kończy się wszystko w kolejce, program główny nie wychodzi. Muszę nacisnąć CTRL^C.

Co robię źle?

Dzięki.

Na marginesie: jeśli jest coś, co można poprawić, proszę daj mi znać. Proste rzeczy, takie jak sprawdzanie, kiedy kolejka jest pusta, itp.

Odpowiedz

5

Myślę, że powinieneś używać JoinableQueue jak w przykładzie z dokumentacji.

import gevent 
from gevent.queue import * 
import time 
import random 

q = JoinableQueue() 
workers = [] 

def do_work(wid, value): 
    gevent.sleep(random.randint(0,2)) 
    print 'Task', value, 'done', wid 

def worker(wid): 
    while True: 
     item = q.get() 
     try: 
      do_work(wid, item) 
     finally: 
      q.task_done() 


def producer(): 
    for i in range(4): 
     workers.append(gevent.spawn(worker, random.randint(1, 100000))) 

    for item in range(1, 9): 
     q.put(item) 

producer() 
q.join() 
+0

Czy mógłbyś wyjaśnić, co robiłem źle? Twoje rozwiązanie działa dobrze, ale dobrze by było wiedzieć. Dzięki. –

+1

@MridangAgarwalla, Nie jestem zaznajomiony z wewnętrznymi wersjami greenletów, ale myślę, że 'q.get()' z domyślnymi parametrami 'block = True, timeout = None' będzie blokował na zawsze w pustej kolejce. Na przykład, używam gevent-1.0b1.win32, i to podnosi 'gevent.hub.LoopExit: Ta operacja będzie blokować na zawsze wyjątek, kiedy próbuję zrobić' queue.get() 'w pustej' kolejce' w tym kod http://pastebin.com/mduShJBs – reclosedev

2

W swoim zakładzie aktywujesz pętlę, która będzie działać wiecznie.

Na marginesie, imho bardziej elegancki „zawsze pętla” mogą być pisane tylko:

for work_unit in q: 
    # Do work, etc 

gevent.joinall() czeka na robotników do końca; ale nigdy tego nie robią, więc twój program na zawsze będzie czekał. To powoduje, że nie wychodzi.

Jeśli nie dbają o pracowników już, można po prostu zabić je zamiast:

gevent.killall(workers) 

Alternatywą jest umieszczenie „specjalną” pozycję w kolejce. Kiedy pracownik otrzymuje ten przedmiot, rozpoznaje go jako odmienny od normalnej pracy i przestaje działać.

for worker in workers: 
    q.put("TimeToDie") 

for work_unit in q: 
    if work_unint == "TimeToDie": 
     break 
    do_work() 

Albo możesz użyć Eventu Geventa do wykonania tego rodzaju wzoru.

+3

Możesz użyć 'StopIteration' zamiast napisu' "TimeToDie" ', to pozwala' na work_unit w q: 'przerwać, kiedy osiągnie' StopIteration'. – reclosedev

+0

@reclosedev Wow, tak, to naprawdę niezły pomysł. – Ivo