Мне нужна очередь, в которую может помещаться несколько потоков, и из которых могут считываться несколько потоков.
В Python есть как минимум два класса очереди, Queue.Queue и collection.deque, причем первый, по-видимому, использует второй для внутреннего использования. Оба утверждают, что они являются потокобезопасными в документации.
Тем не менее, документы очереди также утверждают:
collection.deque - это альтернативная реализация неограниченных очередей с быстрыми атомарными операциями append () и popleft (), которые не требуют блокировки.
Что, я думаю, я не совсем понимаю: означает ли это, что deque в конце концов не является полностью поточно-ориентированным?
Если это так, я не могу полностью понять разницу между двумя классами. Я вижу, что очередь добавляет функциональность блокировки. С другой стороны, он теряет некоторые скрытые функции, такие как поддержка оператора.
Прямой доступ к внутреннему объекту deque
х в очереди (). deque
потокобезопасный?
Кроме того, почему Queue использует мьютекс для своих операций, когда deque уже поточно-ориентирован?
источник
RuntimeError: deque mutated during iteration
то, что вы могли бы получить, это использование общегоdeque
потока между несколькими потоками и отсутствие блокировки ...deque
время, повторяя даже в том же потоке. Единственная причина, по которой вы не можете получить эту ошибку,Queue
заключается в том, чтоQueue
она не поддерживает итерацию.Ответы:
Queue.Queue
иcollections.deque
служат различным целям. Queue.Queue предназначен для обеспечения возможности взаимодействия различных потоков с использованием сообщений / данных, находящихся в очереди, тогда какcollections.deque
он просто предназначен в качестве структуры данных. Вот почемуQueue.Queue
есть такие методы , какput_nowait()
,get_nowait()
иjoin()
, в то время какcollections.deque
не делает.Queue.Queue
не предназначен для использования в качестве коллекции, поэтому ему не хватает подобныхin
операторов.Это сводится к следующему: если у вас есть несколько потоков, и вы хотите, чтобы они могли общаться без необходимости блокировок, вы ищете
Queue.Queue
; если вы просто хотите использовать очередь или двустороннюю очередь в качестве структуры данных, используйтеcollections.deque
.Наконец, доступ к внутренней деке a и манипулирование ею -
Queue.Queue
это игра с огнем - вы действительно не хотите этого делать.источник
Queue.Queue
, он используетdeque
под капотом.collections.deque
это коллекция, аQueue.Queue
это механизм связи.Queue.Queue
Сверхзадача в том, чтобы сделать его поточно-ориентированным. Использованиеdeque
для общения между потоками приведет только к болезненным гонкам. Всякий раз, когдаdeque
оказывается поточнобезопасным, это счастливая случайность того, как реализован интерпретатор, а не то, на что можно положиться. Вот почемуQueue.Queue
существует в первую очередь.deque is threadsafe by accident due to the existence of GIL
; Это правда, чтоdeque
GIL обеспечивает безопасность потоков, но это не такby accident
. В официальной документации на python четко указано, что методыdeque
pop*
/append*
являются потокобезопасными. Таким образом, любая действительная реализация на python должна предоставлять такую же гарантию (реализации без GIL должны будут выяснить, как это сделать без GIL). Вы можете смело полагаться на эти гарантии.deque
для общения. Если вы обернетесьpop
вtry/except
, вы закончите с занятым циклом, поглощающим огромное количество процессоров, ожидающих новых данных. Это кажется ужасно неэффективным подходом по сравнению с предлагаемыми блокирующими вызовамиQueue
, которые гарантируют, что поток, ожидающий данные, перейдет в спящий режим и не будет тратить время ЦП.Queue.Queue
то, потому что это написано с помощьюcollections.deque
: hg.python.org/cpython/file/2.7/Lib/Queue.py - он использует переменные условия для эффективного позволитьdeque
его обтекает доступ за границы потока безопасно и эффективно. Объяснение того, как вы использовали быdeque
для общения, прямо там, в источнике.Если все, что вам нужно, это потокобезопасный способ переноса объектов между потоками , тогда оба будут работать (как для FIFO, так и для LIFO). Для FIFO:
Queue.put()
иQueue.get()
потокобезопасныdeque.append()
иdeque.popleft()
потокобезопасныПримечание:
deque
не могут быть потокобезопасными, я не уверен.deque
не блокируетсяpop()
или,popleft()
таким образом, вы не можете основывать поток своих потребительских потоков на блокировке, пока не прибудет новый элемент.Однако, похоже, что deque имеет значительное преимущество в эффективности . Вот некоторые результаты тестов в секундах с использованием CPython 2.7.3 для вставки и удаления 100 тыс. Элементов
Вот эталонный код:
источник
deque
не могут быть потокобезопасными». Откуда ты это взял?Для информации есть билет Python, на который ссылаются для безопасности потоков deque ( https://bugs.python.org/issue15329 ). Заголовок «уточнить, какие методы deque являются поточно-ориентированными»
Итог здесь: https://bugs.python.org/issue15329#msg199368
В любом случае, если вы не уверены на 100% и предпочитаете надежность, а не производительность, просто поставьте «лайк»;)
источник
Все одноэлементные методы
deque
являются атомарными и потокобезопасными. Все остальные методы также поточно-ориентированы. Такие вещи, какlen(dq)
,dq[4]
дают мгновенные правильные значения. Но подумайте, например, о том, чтоdq.extend(mylist)
: вы не получаете гарантию того, что все элементыmylist
находятся в строке, когда другие потоки также добавляют элементы на одной стороне, но обычно это не является обязательным требованием для взаимодействия между потоками и для поставленной задачи.Таким образом, a
deque
в ~ 20 раз быстрее, чемQueue
(который используетdeque
скрытую структуру), и если вам не нужен «удобный» API синхронизации (блокировка / тайм-аут), строгоеmaxsize
выполнение или «Переопределите эти методы (_put, _get, ..»). ) для реализации поведения подклассов в других организациях очередей , или когда вы сами позаботитесь о таких вещах, тогда голоеdeque
является хорошим и эффективным решением для высокоскоростной связи между потоками.Фактически интенсивное использование дополнительных мьютексов, дополнительных методов
._get()
и т. Д., Вызываемых методамиQueue.py
, обусловлено ограничениями обратной совместимости, прошлым чрезмерным дизайном и отсутствием заботы о предоставлении эффективного решения этой важной проблемы узкого места в скорости при межпотоковой связи. Список использовался в более старых версиях Python, но даже list.append () /. Pop (0) был и является атомарным и безопасным для потоков ...источник
Добавление
notify_all()
к каждомуdeque
append
иpopleft
приводит к гораздо худшим результатам дляdeque
чем улучшение 20х достигается по умолчаниюdeque
поведения :@Jonathan немного изменил свой код, и я получил эталонный тест с использованием cPython 3.6.2 и добавил условие в цикл deque для имитации поведения, которое делает Queue.
И, похоже, производительность ограничена этой функцией
condition.notify_all()
источник
deque
Потокобезопасен. «Операции, которые не требуют блокировки» означает, что вам не нужно делать блокировку самостоятельно, всеdeque
заботится об этом.Взглянув на
Queue
источник, вызывается внутренняя deque, котораяself.queue
использует мьютекс для аксессоров и мутаций, поэтому неQueue().queue
является поточно-ориентированным для использования.Если вы ищете оператор «in», то, вероятно, не самая подходящая структура данных для вашей проблемы - очередь или очередь.
источник
(кажется, у меня нет репутации, чтобы комментировать ...) Вы должны быть осторожны, какие методы deque вы используете из разных потоков.
deque.get () кажется потокобезопасным, но я обнаружил, что делать
может потерпеть неудачу, если другой поток добавляет элементы одновременно. Я получил RuntimeException, который жаловался на "deque muted во время итерации".
Проверьте Коллекции модуля.c, чтобы увидеть, какие операции влияют на это
источник
>>> di = {1:None} >>> for x in di: del di[x]
while
цикла.