Очередь.Очередь против коллекций.двусторонняя очередь
Мне нужна очередь, в которую несколько потоков могут помещать материал, и несколько потоков могут читать.
Python имеет как минимум два класса очереди, Queue.Очередь и коллекции.дек, причем первый, по-видимому, использует второй внутренне. Оба утверждают, что являются потокобезопасными в документации.
однако в документах очереди также указано:
коллекций.дека является альтернативой реализация неограниченных очередей с помощью fast atomic append() и popleft() операции, которые не требуется блокировка.
что, я думаю, я не совсем понимаю: означает ли это, что deque не полностью потокобезопасен в конце концов?
Если это так, я не могу полностью понять разницу между двумя классами. Я вижу, что очередь добавляет функциональность блокировки. С другой стороны, он теряет некоторые функции deque, такие как поддержка in-operator.
доступ к внутреннему объекту deque напрямую, это
x в Очередь.)(дек!--1-->
потокобезопасным?
кроме того, почему очередь использует мьютекс для своих операций, когда deque уже потокобезопасен?
7 ответов
Queue.Queue
и collections.deque
служат для разных целей. Очередь.Очередь предназначена для обеспечения взаимодействия различных потоков с помощью сообщений/данных в очереди, тогда как collections.deque
просто предназначен как datastructure. Вот почему Queue.Queue
имеет такие методы, как put_nowait()
, get_nowait()
и join()
, а collections.deque
нет. Queue.Queue
не предназначен для использования в качестве коллекции, поэтому ему не хватает подобных in
оператора.
это сводится к следующему: если у вас есть несколько потоков, и вы хотите, чтобы они могли общаться без необходимости блокировки, вы ищете Queue.Queue
; Если вы просто хотите очередь или двусторонняя очередь в качестве структуры данных, используйте collections.deque
.
наконец, доступ и манипулирование внутренним Деком Queue.Queue
играет с огнем - вы действительно не хотите этого делать.
если все, что вы ищете-это потокобезопасный способ передачи объектов между потоками, тогда оба будут работать (как для FIFO, так и для LIFO). ФИФО:
Queue.put()
иQueue.get()
являются потокобезопаснымиdeque.append()
иdeque.popleft()
являются потокобезопасными
Примечание:
- прочие операции на
deque
не может быть потокобезопасным, я не уверен. -
deque
не блокpop()
илиpopleft()
таким образом, вы не можете основывать поток потока потребителей на блокировке до прибытия нового элемента.
однако, кажется, что deque имеет значительное преимущество эффективности. Вот некоторые результаты тестов в секундах с помощью CPython 2.7.3 для вставки и удаления 100k элементов
deque 0.0747888759791
Queue 1.60079066852
вот контрольный код:
import time
import Queue
import collections
q = collections.deque()
t0 = time.clock()
for i in xrange(100000):
q.append(1)
for i in xrange(100000):
q.popleft()
print 'deque', time.clock() - t0
q = Queue.Queue(200000)
t0 = time.clock()
for i in xrange(100000):
q.put(1)
for i in xrange(100000):
q.get()
print 'Queue', time.clock() - t0
для информации есть билет Python, на который ссылается deque thread-safety (https://bugs.python.org/issue15329). Название "уточните, какие методы deque являются потокобезопасными"
итог здесь:https://bugs.python.org/issue15329#msg199368
двухсторонняя очередь это функции append(), appendleft(), поп(), popleft () и лен(д) операции потокобезопасны в CPython. Методы append имеют DECREF в конце (для случаев, когда maxlen имеет было установлено), но это происходит после всех обновления структуры и инварианты были восстановлены, поэтому можно обрабатывать эти операции как атомный.
в любом случае, если вы не уверены на 100% и предпочитаете надежность производительности, просто поставьте блокировку ;)
deque
является потокобезопасным. "операции, которые не требуют блокировки" означает, что вы не должны делать блокировку самостоятельно,deque
позаботится об этом.
на Queue
источник, внутренний deque называется self.queue
и использует мьютекс для аксессоров и мутаций, так что Queue().queue
is не thread-safe для использования.
если вы ищете оператор "in" , то deque или queue, возможно, не самая подходящая структура данных для вашего проблема.
deque 0.469802
Queue 0.667279
@Jonathan немного модифицирует свой код, и я получаю тест с помощью cPython 3.6.2 и добавляю условие в цикл deque для имитации очереди поведения.
import time
from queue import Queue
import threading
import collections
mutex = threading.Lock()
condition = threading.Condition(mutex)
q = collections.deque()
t0 = time.clock()
for i in range(100000):
with condition:
q.append(1)
condition.notify_all()
for _ in range(100000):
with condition:
q.popleft()
condition.notify_all()
print('deque', time.clock() - t0)
q = Queue(200000)
t0 = time.clock()
for _ in range(100000):
q.put(1)
for _ in range(100000):
q.get()
print('Queue', time.clock() - t0)
и кажется, что производительность ограничена
эта функция condition.notify_all()
сборники.deque-это альтернативная реализация неограниченных очередей с операциями fast atomic append() и popleft (), которые не требуют блокировки. очереди документы
(Кажется, у меня нет репутации, чтобы комментировать...) Вы должны быть осторожны, какие методы deque вы используете из разных потоков.
deque.get () кажется threadsafe, но я обнаружил, что делаю
for item in a_deque:
process(item)
может произойти сбой, если другой поток добавляет элементы одновременно. Я получил RuntimeException, который жаловался "deque мутировал во время итерации".
Регистрация collectionsmodule.c чтобы увидеть, какие операции будут затронуты этим
все одноэлементные методы на deque
атомная и потокобезопасным. Все остальные методы также потокобезопасны. Такие вещи, как len(dq)
, dq[4]
дайте мгновенные правильные значения. Но подумайте, например, о dq.extend(mylist)
: вы не получаете гарантию того, что все элементы mylist
хранятся в строке, когда другие потоки также добавляют элементы на той же стороне - но это обычно не требование в межпотоковой связи и для поставленной задачи.
так deque
~20x быстрее, чем Queue
(который использует deque
под капотом) и если вам не нужен" удобный " API синхронизации (блокировка / тайм-аут), строгий maxsize
подчинение или "переопределить эти методы (_put, запросе, ..) реализовать другие организации очереди" поведение подкласса, или когда вы заботитесь о таких вещах сами, тогда голый deque
хорошее и эффективное дело для высокоскоростного сообщения Интер-потока.
на самом деле тяжелое использование дополнительного мьютекса и дополнительный метод ._get()
etc. метод вызывает Queue.py
из-за ограничений обратной совместимости, прошлого чрезмерного дизайна и отсутствия заботы о предоставлении эффективного решения этой важной проблемы узкого места скорости в межпотоковой связи. Список использовался в более старых версиях Python - но даже список.добавлять./)(pop (0) был и является атомарным и threadsafe ...