Очередь.Очередь против коллекций.двусторонняя очередь



Мне нужна очередь, в которую несколько потоков могут помещать материал, и несколько потоков могут читать.



Python имеет по крайней мере два класса очереди, очереди.Очередь и коллекции.дек, причем первый, по-видимому, использует последний внутренне. Оба утверждают, что потокобезопасны в документации.



однако в документах очереди также указано:




коллекций.дека является альтернативой
реализация неограниченных очередей
с быстрым атомарным добавлением () и
popleft() операции, которые не
требуется блокировка.




что я думаю, я не совсем понимаю: означает ли это, что deque не полностью потокобезопасен в конце концов?



Если это так, я не могу полностью понять разницу между двумя классами. Я вижу, что очередь добавляет функциональность блокировки. С другой стороны, он теряет некоторые функции deque, такие как поддержка in-operator.



доступ к объекту внутреннего очереди напрямую, составляет




x in Очередь.)(дек




потокобезопасным?



кроме того, почему Queue использует мьютекс для своих операций, когда deque уже является потокобезопасным?

662   7  

7 ответов:

Queue.Queue и collections.deque служат для разных целей. Очередь.Очередь предназначена для того, чтобы позволить различным потокам взаимодействовать с использованием сообщений/данных в очереди, тогда как collections.deque просто предназначен в качестве структуры данных. Вот почему Queue.Queue имеет такие методы, как put_nowait(),get_nowait() и join(), а collections.deque нет. Queue.Queue не предназначен для использования в качестве коллекции, поэтому ему не хватает подобных in оператора.

это сводится к следующему: если у вас есть несколько потоков, и вы хотите, чтобы они могли общаться без необходимости блокировки, вы ищете Queue.Queue; Если вам просто нужна очередь или двойная очередь в качестве структуры данных, используйте collections.deque.

наконец, доступ и управление внутренним Деком a Queue.Queue играет с огнем - вы действительно не хотите этого делать.

если все, что вы ищете-это потокобезопасный способ передачи объектов между потоками, тогда оба будут работать (как для FIFO, так и для LIFO). Для ФИФО:

Примечание:

  • прочие операции на 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

для информации есть авиабилет питона ссылается на потокобезопасность двухсторонней очередью (https://bugs.python.org/issue15329). Название "уточните, какие методы deque являются потокобезопасными"

итог здесь:https://bugs.python.org/issue15329#msg199368

двухсторонняя очередь это функции append(), appendleft(), поп(), popleft () и лен(д) операции являются потокобезопасными в с CPython. Методы добавления имеют DECREF в конце (для случаев, когда maxlen имеет было установлено), но это происходит после всех обновления структуры и инварианты были восстановлены, так что это нормально для лечения этих операций как атомный.

в любом случае, если вы не уверены на 100% и вы предпочитаете надежность и производительность, поставьте лайк замок ;)

deque является потокобезопасным. "операции, которые не требуют блокировки" означает, что вы не должны делать блокировку самостоятельно,deque заботится о ней.

на Queue источник, внутренний дек называется self.queue и использует мьютекс для аксессоров и мутаций, так что Queue().queue и не нитки-безопасная в использовании.

если вы ищете оператор "in", то deque или queue, возможно, не самая подходящая структура данных для вашего проблема.

deque 0.469802
Queue 0.667279

@Джонатан немного модифицирует свой код, и я получаю тест с помощью 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-это альтернативная реализация неограниченных очередей с быстрыми атомарными операциями append() и popleft (), которые не требуют блокировки. очереди документы

(Кажется, у меня нет репутации, чтобы комментировать...) Вы должны быть осторожны, какие методы дека вы используете из разных потоков.

дека.get () кажется потокобезопасным, но я обнаружил, что делаю

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-но даже список.добавлять./)(поп(0) был и атомной и ориентированы на многопотоковое исполнение ...

Comments

    Ничего не найдено.