2012-03-14 8 views
7

używam dość standardowy Threading.Event: Główny wątek trafia do punktu, gdzie jej w pętli, który działa:Lepsze rozwiązanie dla Pythona Threading.Event semi-zajęty czeka

event.wait(60) 

innych bloków na wniosek aż post jest dostępne, a następnie inicjuje:

event.set() 

Spodziewam się główny wątek, aby wybrać do 40 sekund, ale to nie jest przypadek. Od źródła Python 2.7 lib/threading.py:

# Balancing act: We can't afford a pure busy loop, so we 
# have to sleep; but if we sleep the whole timeout time, 
# we'll be unresponsive. The scheme here sleeps very 
# little at first, longer as time goes on, but never longer 
# than 20 times per second (or the timeout time remaining). 
endtime = _time() + timeout 
delay = 0.0005 # 500 us -> initial delay of 1 ms 
while True: 
    gotit = waiter.acquire(0) 
    if gotit: 
     break 
    remaining = endtime - _time() 
    if remaining <= 0: 
     break 
    delay = min(delay * 2, remaining, .05) 
    _sleep(delay) 

Co dostajemy to wybierz syscall kursują co 500us. Powoduje to zauważalne obciążenie maszyny przy dość ciasnej pętli wyboru.

Czy ktoś mógłby wyjaśnić, dlaczego jest balansowanie zaangażowany i dlaczego jest ona inna niż nitka czeka na deskryptorze pliku.

i po drugie, czy istnieje lepszy sposób na wdrożenie głównie śpiącego wątku głównego bez tak ciasnej pętli?

Odpowiedz

3

Niedawno został uderzony przez tego samego problemu, a ja też go wytropił na taką bloku kodu w module threading.

To jest do bani.

Rozwiązaniem byłoby albo przeciążenie modułu wątków, albo migracja do python3, gdzie ta część implementacji została naprawiona.

W moim przypadku migracja do Pythona3 byłaby ogromnym wysiłkiem, więc wybrałem ten pierwszy. Co zrobiłem było:

  1. stworzyłem krótki .so plik (używając cython) z interfejsem do pthread. Zawiera funkcje Pythona, które wywołują odpowiednie funkcje pthread_mutex_* i łącza z libpthread. W szczególności funkcją najbardziej odpowiednią do zadania, które nas interesuje, jest pthread_mutex_timedlock.
  2. Stworzyłem nowy moduł threading2 (i zastąpiłem wszystkie linie import threading w mojej bazie z import threading2). W threading2, ja ponownie zdefiniowane wszystkie istotne zajęcia z threading (Lock, Condition, Event), a także te z Queue które używają dużo (Queue i PriorityQueue). Klasa Lock została całkowicie ponownie zaimplementowana przy użyciu funkcji pthread_mutex_*, ale reszta była znacznie łatwiejsza - po prostu podklasowałem oryginał (na przykład threading.Event) i przesłano __init__, aby utworzyć nowy typ Lock. Reszta po prostu działała.

Wdrożenie nowej Lock typu była bardzo podobna do oryginalnej implementacji w threading, ale w oparciu o nową implementacja z acquire na kodzie znalazłem w threading modułu python3 „s (co, oczywiście, jest znacznie prostsze niż wyżej wymieniony blok "bilansowania"). Ta część była dość łatwa.

(Przy okazji wynik w moim przypadku wyniósł 30% przyspieszenia mojego procesu wielowątkowego, nawet więcej niż się spodziewałem.)

2

Całkowicie się z tobą zgadzam, to jest kulawa.

Obecnie mam trzymać z prostym wybierz rozmowy bez limitu czasu i słuchanie na rurze utworzonej wcześniej. Budzenie polega na wpisaniu znaku w potoku.

Zobacz funkcje sleep i wakeup z gunicorn.

Powiązane problemy