Potrzebuję kolejki, w której wiele wątków może umieszczać różne rzeczy, z których wiele wątków może czytać.
Python ma co najmniej dwie klasy kolejek, Queue.Queue i collections.deque, przy czym ta pierwsza najwyraźniej wykorzystuje tę drugą wewnętrznie. Oba twierdzą, że są bezpieczne w wątku w dokumentacji.
Jednak dokumenty kolejki również stwierdzają:
collections.deque to alternatywna implementacja niezwiązanych kolejek z szybkimi atomowymi operacjami append () i popleft (), które nie wymagają blokowania.
Co chyba nie do końca rozumiem: czy to znaczy, że deque nie jest w pełni bezpieczny dla wątków?
Jeśli tak, może nie do końca rozumiem różnicę między dwiema klasami. Widzę, że Kolejka dodaje funkcje blokowania. Z drugiej strony traci niektóre funkcje deque, takie jak wsparcie dla operatora.
Bezpośredni dostęp do wewnętrznego obiektu deque jest
x w Queue (). deque
bezpieczny dla wątków?
Ponadto, dlaczego Queue używa muteksu do swoich operacji, skoro deque jest już bezpieczny dla wątków?
źródło
RuntimeError: deque mutated during iteration
to, co możesz uzyskać, to korzystanie ze wspólnegodeque
między kilkoma wątkami i bez blokowania ...deque
podczas iteracji nawet w tym samym wątku. Jedynym powodem, dla którego nie można uzyskać tego błędu,Queue
jest to, żeQueue
nie obsługuje iteracji.Odpowiedzi:
Queue.Queue
icollections.deque
służą różnym celom. Queue.Queue jest przeznaczony do umożliwienia komunikacji między różnymi wątkami za pomocą wiadomości / danych w kolejce, podczas gdycollections.deque
jest po prostu przeznaczony jako struktura danych. DlategoQueue.Queue
ma metod, takich jakput_nowait()
,get_nowait()
ijoin()
, natomiastcollections.deque
nie.Queue.Queue
nie jest przeznaczony do wykorzystania jako kolekcja, dlatego brakuje mu tegoin
operatora.Sprowadza się to do tego: jeśli masz wiele wątków i chcesz, aby mogły komunikować się bez potrzeby blokowania, szukasz
Queue.Queue
; jeśli chcesz po prostu kolejki lub kolejki podwójnie zakończonej jako struktury danych, użyjcollections.deque
.Wreszcie, dostęp do wewnętrznego deque i manipulowanie nim
Queue.Queue
to zabawa z ogniem - naprawdę nie chcesz tego robić.źródło
Queue.Queue
, używa onodeque
pod maską.collections.deque
jest zbiorem, aQueue.Queue
mechanizmem komunikacji. Koszty ogólneQueue.Queue
mają sprawić, że będzie wątkowo bezpieczne. Używaniedeque
do komunikacji między wątkami doprowadzi tylko do bolesnych wyścigów. Ilekroćdeque
zdarza się, że jest to bezpieczny wątek, jest to szczęśliwy wypadek, w jaki sposób tłumacz jest implementowany, i nie można na nim polegać. Dlatego właśnieQueue.Queue
istnieje.deque is threadsafe by accident due to the existence of GIL
; to prawda, żedeque
polega na GIL w celu zapewnienia bezpieczeństwa wątków - ale tak nie jestby accident
. Oficjalna dokumentacja Pythona wyraźnie stwierdza, żedeque
pop*
/append*
metody są bezpieczne dla wątków. Tak więc każda poprawna implementacja Pythona musi zapewniać tę samą gwarancję (implementacje bez GIL będą musiały dowiedzieć się, jak to zrobić bez GIL). Możesz bezpiecznie polegać na tych gwarancjach.deque
do komunikacji. Jeśli zamknieszpop
się wtry/except
, skończysz z zajętą pętlą, która pochłonie ogromną ilość procesora, tylko czekając na nowe dane. To wydaje się być bardzo nieefektywnym podejściem w porównaniu z oferowanymi przez blokowanie wywołaniamiQueue
, które zapewniają, że wątek oczekujący na dane przejdzie w tryb uśpienia i nie zmarnuje czasu procesora.Queue.Queue
, ponieważ jest on napisany przy użyciucollections.deque
: hg.python.org/cpython/file/2.7/Lib/Queue.py - używa zmiennych warunkowych, aby efektywnie umożliwićdeque
dostęp do zawijania ponad granicami gwintu bezpiecznie i skutecznie. Wyjaśnienie, w jaki sposób chcesz użyćdeque
do komunikacji, znajduje się w źródle.Jeśli wszystko, czego szukasz, to bezpieczny dla wątków sposób przenoszenia obiektów między wątkami , oba działałyby (zarówno dla FIFO, jak i LIFO). W przypadku FIFO:
Queue.put()
iQueue.get()
są bezpieczne dla wątkówdeque.append()
ideque.popleft()
są bezpieczne dla wątkówUwaga:
deque
mogą nie być bezpieczne dla wątków, nie jestem pewien.deque
nie blokuje się napop()
lubpopleft()
nie można więc oprzeć przepływ wątek konsumentów na blokowanie aż nowa pozycja przybywa.Wydaje się jednak, że deque ma znaczną przewagę wydajności . Oto kilka wyników testu porównawczego w sekundach przy użyciu CPython 2.7.3 do wstawiania i usuwania 100 000 elementów
Oto kod testu porównawczego:
źródło
deque
mogą nie być bezpieczne dla wątków”. Skąd to masz?Aby uzyskać informacje, istnieje bilet w języku Python, do którego odwołuje się deque thread-safety ( https://bugs.python.org/issue15329 ). Tytuł „wyjaśnij, które metody deque są bezpieczne dla wątków”
Podsumowując: https://bugs.python.org/issue15329#msg199368
W każdym razie, jeśli nie jesteś w 100% pewien i wolisz niezawodność od wydajności, po prostu wstaw blokadę;)
źródło
Wszystkie metody jednoelementowe
deque
są atomowe i bezpieczne dla wątków. Wszystkie pozostałe metody są również wątkowo bezpieczne. Rzeczy takielen(dq)
, jakdq[4]
chwilowe poprawne wartości. Ale pomyśl np. Odq.extend(mylist)
: nie otrzymujesz gwarancji, że wszystkie elementy wmylist
są zapisywane w rzędzie, gdy inne wątki również dołączają elementy po tej samej stronie - ale zwykle nie jest to wymagane w komunikacji między wątkami i dla kwestionowanego zadania.Tak więc a
deque
jest ~ 20 razy szybszy niżQueue
(który używadeque
maskowania) i chyba, że nie potrzebujesz „wygodnego” API synchronizacji (blokowanie /maxsize
przekroczenie limitu czasu), ścisłego przestrzegania lub „Zastąp te metody (_put, _get, .. ), aby wdrożyć zachowanie podklasowania innych organizacji kolejek lub gdy sam zajmiesz się takimi sprawami,deque
to dobry i skuteczny sposób na szybką komunikację między wątkami.W rzeczywistości duże użycie dodatkowej metody mutex i dodatkowej metody
._get()
itp.Queue.py
Jest spowodowane ograniczeniami kompatybilności wstecznej, przeprojektowaniem w przeszłości i brakiem dbałości o zapewnienie skutecznego rozwiązania tego ważnego problemu wąskiego gardła w komunikacji między wątkami. Lista była używana w starszych wersjach Pythona - ale nawet list.append () /. Pop (0) był i jest atomowy i bezpieczny dla wątków ...źródło
Dodanie
notify_all()
do każdegodeque
append
ipopleft
wyników w znacznie gorszych wynikówdeque
niż poprawa 20x osiągnąć domyślnegodeque
zachowania :@Jathanathan zmodyfikuje trochę swój kod, a ja otrzymuję test porównawczy za pomocą cPython 3.6.2 i dodam warunek w pętli deque, aby zasymulować zachowanie kolejki.
I wydaje się, że wydajność ograniczona przez tę funkcję
condition.notify_all()
źródło
deque
jest bezpieczny dla wątków. „Operacje, które nie wymagają blokowania” oznaczają, że nie musisz sam blokować,deque
zajmuje się tym.Patrząc na
Queue
źródło, wywoływana jest wewnętrzna dequeself.queue
i używa muteksu dla akcesoriów i mutacji, więc tak nieQueue().queue
jest bezpieczny w wątkach.Jeśli szukasz operatora „w”, deque lub kolejka prawdopodobnie nie są najbardziej odpowiednią strukturą danych dla twojego problemu.
źródło
(wygląda na to, że nie mam reputacji do komentowania ...) Musisz uważać, jakich metod deque używasz z różnych wątków.
deque.get () wydaje się być wątkowo bezpieczny, ale znalazłem, że to działa
może się nie powieść, jeśli inny wątek dodaje elementy w tym samym czasie. Dostałem wyjątek RuntimeException, który narzekał „deque zmutowany podczas iteracji”.
Sprawdź kolekcjemodule.c, aby zobaczyć, na które operacje ma to wpływ
źródło
>>> di = {1:None} >>> for x in di: del di[x]
while
pętlą.