Zauważyłem, że często sugeruje się używanie kolejek z wieloma wątkami zamiast list i .pop()
. Czy dzieje się tak, ponieważ listy nie są bezpieczne dla wątków lub z innego powodu?
155
Zauważyłem, że często sugeruje się używanie kolejek z wieloma wątkami zamiast list i .pop()
. Czy dzieje się tak, ponieważ listy nie są bezpieczne dla wątków lub z innego powodu?
Odpowiedzi:
Same listy są bezpieczne dla wątków. W CPythonie GIL chroni przed równoczesnym dostępem do nich, a inne implementacje dbają o użycie precyzyjnej blokady lub zsynchronizowanego typu danych dla ich implementacji list. Jednakże, podczas gdy list sami nie mogą przejść przez uszkodzony prób do równoczesnego dostępu listach męska dane nie są chronione. Na przykład:
nie ma gwarancji, że faktycznie zwiększy L [0] o jeden, jeśli inny wątek zrobi to samo, ponieważ
+=
nie jest to operacja atomowa. (Bardzo, bardzo niewiele operacji w Pythonie jest w rzeczywistości atomowych, ponieważ większość z nich może spowodować wywołanie dowolnego kodu w Pythonie). Powinieneś używać kolejek, ponieważ jeśli używasz tylko niezabezpieczonej listy, możesz uzyskać lub usunąć niewłaściwy element z powodu rasy warunki.źródło
Aby wyjaśnić punkt w doskonałej odpowiedzi Thomasa, należy wspomnieć, że
append()
jest bezpieczny dla wątków.Dzieje się tak, ponieważ nie ma obaw, że odczytywane dane będą w tym samym miejscu, gdy zaczniemy je pisać .
append()
Operacja nie odczytuje dane, to tylko zapisuje je na liście.źródło
PyList_Append
odbywa się w jednym zamku GIL. Otrzymuje odniesienie do obiektu do dołączenia. Zawartość tego obiektu może ulec zmianie po jego ocenie i przed wykonaniem wywołaniaPyList_Append
. Ale nadal będzie to ten sam obiekt i bezpiecznie dołączony (jeśli to zrobiszlst.append(x); ok = lst[-1] is x
,ok
może to być oczywiście Fałsz). Kod, do którego się odwołujesz, nie czyta z dołączonego obiektu, z wyjątkiem jego ZWIĘKSZENIA. Czyta listę, do której jest dołączona, i może ją ponownie przydzielić.L[0] += x
wykona__getitem__
onL
a potem__setitem__
naL
- jeśliL
podpory__iadd__
będzie robić rzeczy trochę inaczej na styku obiektu, ale nadal istnieją dwie odrębne operacje naL
na poziomie interpreter Pythona (widać je w kompilowany kod bajtowy).append
Odbywa się aa pojedynczego wywołania metody w kodu bajtowego.remove
?Oto niepełna jeszcze niewyczerpujący wykaz przykładów z
list
działalności i czy nie są one bezpieczne dla wątków. Mając nadzieję na uzyskanie odpowiedzi dotyczącejobj in a_list
konstruktem językowym tutaj .źródło
Niedawno miałem taki przypadek, w którym musiałem stale dołączać do listy w jednym wątku, przeglądać elementy w pętli i sprawdzać, czy element był gotowy, w moim przypadku był to AsyncResult i usunąć go z listy tylko wtedy, gdy był gotowy. Nie mogłem znaleźć żadnych przykładów, które jasno pokazałyby mój problem Oto przykład pokazujący ciągłe dodawanie do listy w jednym wątku i ciągłe usuwanie z tej samej listy w innym wątku. Wadliwa wersja działa łatwo na mniejszych liczbach, ale utrzymuj liczby wystarczająco duże i uruchom kilka razy, a zobaczysz błąd
Wersja USZKODZONA
Wyjście, gdy ERROR
Wersja wykorzystująca zamki
Wynik
Wniosek
Jak wspomniano we wcześniejszych odpowiedziach, podczas gdy czynność dołączania lub wyskakiwania elementów z samej listy jest bezpieczna dla wątków, to, co nie jest bezpieczne dla wątków, to dołączanie do jednego wątku i wstawianie do innego
źródło
with r:
) zamiast jawnie dzwonićr.acquire()
ir.release()