Подтвердить что ты не робот

Queue.Queue vs. collection.deque

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

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

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

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

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

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

Доступ к внутреннему объекту deque напрямую,

x в очереди(). deque

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Примечание:

  • Другие операции над 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

Ответ 3

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

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

Если вы ищете оператора "in", то дека или очередь, возможно, не самая подходящая структура данных для вашей проблемы.

Ответ 4

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

Нижняя строка здесь: https://bugs.python.org/issue15329#msg199368

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

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

Ответ 5

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

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

for item in a_deque:
   process(item)

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

Отметьте collectionsmodule.c, чтобы увидеть, какие действия затронуты этим

Ответ 6

Все одноэлементные методы на deque являются атомарными и потокобезопасными. Все остальные методы также являются потокобезопасными. Такие вещи, как len(dq), dq[4], дают мгновенные правильные значения. Но подумайте, например. about dq.extend(mylist): вы не получаете гарантию, что все элементы в mylist подаются в строку, когда другие потоки также добавляют элементы на одной стороне - но это обычно не является обязательным требованием в межпоточной связи и для опрошенных задача.

Итак, deque ~ 20 раз быстрее, чем Queue (который использует deque под капотом), и если вам не нужен "удобный" интерфейс синхронизации (блокировка/тайм-аут), строгий maxsize подчинение или "Переопределить эти методы (_put, _get,..), чтобы реализовать другие подсистемы очередей", или когда вы сами заботитесь о таких вещах, то голый deque является хорошим и эффективным решением для высокоскоростная межпоточная связь.

Фактически интенсивное использование дополнительного метода mutex и дополнительного метода ._get() и т.д. в Queue.py вызвано ограничениями обратной совместимости, прошлым чрезмерным дизайном и отсутствием ухаживания за предоставление эффективного решения этой важной скорости проблема узких мест в межпоточной коммуникации. Список использовался в более старых версиях Python, но даже list.append()/. Pop (0) был и является атомарным и потокобезопасным...