Насколько я понимаю, внутренние объекты Queue
в Python обрабатывают блокировку, поэтому вам не нужно использовать внешние блокировки только для вызова .put()
или .get()
. Мой вопрос: покрыт ли я теми же внутренними объектами, если я хочу отобразить содержимое очереди, представив его в виде списка через список (очередь) list(queue.queue)
? Или, в более общем плане, существует ли какая-либо мыслимая операция с использованием очередей Python, которая требует использования внешних по отношению к объекту очереди блокировок для обеспечения безопасности потоков?
Пример:
from queue import Queue
queue = Queue()
list(queue.queue)
Другими словами, придется ли мне когда-нибудь использовать
with lock_object:
some_function(queue)
???
На самом деле, почему бы вам не рассказать нам, чего вы пытаетесь достичь с помощью этого? В Дзене Python: «Должен быть один — и желательно только один — очевидный способ сделать это». & «Если реализацию сложно объяснить, это плохая идея». - У вас может быть лучший выбор дизайна того, что вы пытаетесь сделать, по сравнению с тем, о чем вы просите, создавая эту XY-проблему!
Вы не можете определить, что находится в очереди (кроме ее размера), не вызывая get(), тем самым фактически потребляя ее содержимое. Нет эквивалентной функции «просмотра». Другими словами, если вам нужен список содержимого очереди, вам нужно будет очистить очередь.
@nocomment Я обновил свой вопрос, чтобы использовать правильный синтаксис. Я имел в виду list(queue.queue)
, где .queue
является атрибутом объекта queue
. Мой вопрос заключается в том, является ли доступ к очереди таким образом безопасным для процесса и выполняется ли он атомарно (посредством блокировки или иным образом), когда каждый элемент захватывается до того, как другие процессы смогут изменить его содержимое через .get() или .put().
Непонятно, что вы подразумеваете под словом «очередь». Единственные две стандартные реализации Queue
, о которых я знаю, не могут быть перебраны, поэтому «приведение в виде списка» просто вызывает исключение:
>>> import queue
>>> q = queue.Queue()
>>> list(q)
Traceback (most recent call last):
...
TypeError: 'Queue' object is not iterable
>>> from multiprocessing import Queue
>>> q = Queue()
>>> list(q)
Traceback (most recent call last):
...
TypeError: 'Queue' object is not ierable
Возможно, вам когда-нибудь понадобится сделать:
with lock_object:
some_function(queue)
на основании того, что вы сказали до сих пор, ответить невозможно. Если, например, ваша логика более высокого уровня основана на переводе очереди в (фактически) режим «только для чтения» на некоторое время, тогда, конечно. Вам понадобится блокировка, чтобы обеспечить взаимное исключение между сторонами чтения и записи на время.
.put()
и .get()
сами по себе уже безопасны для потоков и процессов (в случае multiprocessing.Queue
).
Документация не гарантирует больше, чем просто это, поэтому на него нельзя положиться, даже если это «кажется» работает (что, если это так, может быть надежным несчастным случаем конкретной реализации Python, которую вы используете, или может быть непостоянная авария из-за того, что вы просто еще не столкнулись с соответствующими условиями гонки).
Вопрос был отредактирован, чтобы вместо этого спросить о list(queue.queue)
. Это подпадает под более раннюю «несчастность конкретной реализации Python, которую вы используете» в двух отношениях:
Queue.queue
имеет атрибут queue
. Python — это язык «с согласия взрослых», и он не пытается помешать вам использовать детали реализации. Но если вы это сделаете, вы сами по себе. Реализация может измениться в любой момент.list(deque)
сегодня является потокобезопасным (CPython 3.12.5), но это тоже не документировано. Я знаю это только потому, что смотрел на код реализации C. В версии 3.12.6 это может быть небезопасно для потоков. В более общем плане CPython движется к режиму работы «без GIL», в котором подобные вещи с гораздо большей вероятностью будут страдать от гонок.Суть не меняется: .put()
и .get()
сами по себе уже безопасны для потоков и процессов (в случае multiprocessing.Queue
). Ничего большего не гарантируется. Действительно! ;-) Ничего. Если вам нужно нечто большее, чтобы обеспечить надежность в различных реализациях и выпусках, вам потребуется предоставить свои собственные блокировки.
Ах, спасибо @TimPeters, я надеялся, что это сработает так же, как list(deque())
, но на самом деле я это не проверял. Я отредактировал свой вопрос. Похоже, queue.queue
возвращает объект deque, представляющий содержимое очереди. Мой вопрос заключается в том, является ли доступ к очереди таким способом безопасным для процесса и выполняется ли он атомарно (посредством блокировки или иным образом), когда каждый элемент захватывается до того, как другие процессы смогут изменить его содержимое через .get()
или .put()
.
см. новый раздел внизу моего ответа
Что касается недокументированного атрибута queue
, его использование описано в ответе, получившем большое количество голосов: stackoverflow.com/a/38350840/3440745
stackoverflow не определяет язык — его определяют документы Python. Ответ, который я дал, правильный. Ответ, на который ссылаются, — «использовать на свой страх и риск», независимо от того, написано ли это так. Вот документация Python, так что проверьте сами: docs.python.org/3/library/queue.html
@TimPeters, спасибо за объяснение. Если это действительно просто деталь реализации, то, похоже, не существует «безопасного» способа просмотреть снимок содержимого очереди из определенного процесса/потока без получения блокировки, .get()
- выгрузки каждого элемента из очереди и отображения его в список, а затем записать их обратно в очередь, что кажется крайне неэффективным. Я правильно прочитал или что-то пропустил?
Я так не думаю.
list()
приходится перебирать очередь. Хотя отдельные операции являются атомарными, я не думаю, что это заблокирует очередь для всего цикла.