Necesito una cola en la que múltiples hilos puedan poner cosas, y múltiples hilos pueden leer.
Python tiene al menos dos clases de cola, Queue.Queue y collections.deque, y la primera aparentemente usa la segunda internamente. Ambos afirman ser seguros para subprocesos en la documentación.
Sin embargo, los documentos de la cola también indican:
collections.deque es una implementación alternativa de colas ilimitadas con operaciones rápidas atómica append () y popleft () que no requieren bloqueo.
Lo que supongo que no entiendo del todo: ¿significa esto que deque no es completamente seguro para subprocesos después de todo?
Si es así, es posible que no entienda completamente la diferencia entre las dos clases. Puedo ver que Queue agrega funcionalidad de bloqueo. Por otro lado, pierde algunas características antiguas como el soporte para el operador interno.
Acceder al objeto de deque interno directamente, es
x en Queue (). deque
¿a salvo de amenazas?
Además, ¿por qué Queue emplea un mutex para sus operaciones cuando deque ya es seguro para subprocesos?
fuente
RuntimeError: deque mutated during iteration
es lo que podría estar obteniendo es usar undeque
hilo compartido entre varios hilos y sin bloqueo ...deque
tiempo mientras itera incluso en el mismo hilo. La única razón por la que no puede obtener este errorQueue
es queQueue
no admite la iteración.Respuestas:
Queue.Queue
ycollections.deque
sirven para diferentes propósitos. Queue.Queue está destinado a permitir que diferentes hilos se comuniquen usando mensajes / datos en cola, mientrascollections.deque
que simplemente está pensado como una estructura de datos. Es por eso queQueue.Queue
tiene como métodosput_nowait()
,get_nowait()
yjoin()
, mientras quecollections.deque
no lo hace.Queue.Queue
no está destinado a ser utilizado como una colección, por lo que carece de los gustos delin
operador.Se reduce a esto: si tiene múltiples hilos y desea que puedan comunicarse sin la necesidad de bloqueos, está buscando
Queue.Queue
; si solo desea una cola o una cola de doble extremo como estructura de datos, usecollections.deque
.Finalmente, acceder y manipular la deque interna de un
Queue.Queue
juego es jugar con fuego, realmente no quieres estar haciendo eso.fuente
Queue.Queue
, se utilizadeque
debajo del capó.collections.deque
es una colección, mientras queQueue.Queue
es un mecanismo de comunicación. La sobrecargaQueue.Queue
es hacerla segura. Usardeque
para comunicarse entre hilos solo conducirá a carreras dolorosas. Siempredeque
que sea seguro para subprocesos, es un feliz accidente de cómo se implementa el intérprete, y no es algo en lo que se pueda confiar. Por esoQueue.Queue
existe en primer lugar.deque is threadsafe by accident due to the existence of GIL
; Es cierto quedeque
depende de GIL para garantizar la seguridad de los hilos, pero no lo esby accident
. La documentación oficial de Python establece claramente quedeque
pop*
/append*
métodos son seguros para subprocesos. Por lo tanto, cualquier implementación válida de Python debe proporcionar la misma garantía (las implementaciones sin GIL tendrán que descubrir cómo hacerlo sin GIL). Puede confiar con seguridad en esas garantías.deque
para la comunicación. Si se ajustapop
a unatry/except
, terminará con un bucle ocupado que consume una enorme cantidad de CPU solo esperando nuevos datos. Esto parece un enfoque terriblemente ineficiente en comparación con las llamadas de bloqueo que ofreceQueue
, que aseguran que el hilo que espera los datos se suspenda y no pierda el tiempo de la CPU.Queue.Queue
entonces, porque está escrito usandocollections.deque
: hg.python.org/cpython/file/2.7/Lib/Queue.py : utiliza variables de condición para permitirdeque
que se acceda de manera eficiente sobre los límites del hilo de forma segura y eficiente. La explicación de cómo usaría undeque
para la comunicación está ahí en la fuente.Si todo lo que está buscando es una forma segura de subprocesos para transferir objetos entre subprocesos , entonces ambos funcionarían (tanto para FIFO como para LIFO). Para FIFO:
Queue.put()
yQueue.get()
son seguros para subprocesosdeque.append()
ydeque.popleft()
son seguros para subprocesosNota:
deque
no sean seguras para subprocesos, no estoy seguro.deque
no se bloqueapop()
opopleft()
no puede basar su flujo de hilo de consumidor en el bloqueo hasta que llegue un nuevo artículo.Sin embargo, parece que deque tiene una ventaja de eficiencia significativa . Estos son algunos resultados de referencia en segundos utilizando CPython 2.7.3 para insertar y eliminar elementos de 100k
Aquí está el código de referencia:
fuente
deque
pueden no ser seguras para subprocesos". ¿De dónde sacas eso?Para obtener información, hay un ticket de Python al que se hace referencia para deque thread-safety ( https://bugs.python.org/issue15329 ). Título "aclarar qué métodos de extracción son seguros para subprocesos"
En pocas palabras aquí: https://bugs.python.org/issue15329#msg199368
De todos modos, si no está 100% seguro y prefiere la confiabilidad sobre el rendimiento, simplemente coloque un bloqueo similar;)
fuente
Todos los métodos de un solo elemento
deque
son atómicos y seguros para subprocesos. Todos los demás métodos también son seguros para subprocesos. Cosas comolen(dq)
,dq[4]
producen valores correctos momentáneos. Pero piense, por ejemplodq.extend(mylist)
: no se garantiza que todos los elementosmylist
se archiven en una fila cuando otros hilos también agregan elementos en el mismo lado, pero eso generalmente no es un requisito en la comunicación entre hilos y para la tarea cuestionada.Por lo tanto, a
deque
es ~ 20 veces más rápido queQueue
(que usa undeque
oculto) y, a menos que no necesite la API de sincronización "cómoda" (bloqueo / tiempo de espera), la estrictamaxsize
obediencia o la "Anulación de estos métodos (_put, _get, .. ) para implementar el comportamiento de subclasificación de otras organizaciones de colas , o cuando usted se ocupa de esas cosas usted mismo, entonces un simpledeque
es un trato bueno y eficiente para la comunicación entre subprocesos de alta velocidad.De hecho, el uso intensivo de un mutex adicional y un método adicional,
._get()
etc.,Queue.py
se debe a restricciones de compatibilidad con versiones anteriores, sobre diseño pasado y falta de cuidado para proporcionar una solución eficiente para este importante problema de cuello de botella de velocidad en la comunicación entre subprocesos. Se utilizó una lista en versiones anteriores de Python, pero incluso list.append () /. Pop (0) era y es atómica y segura para subprocesos ...fuente
Sumar
notify_all()
a cada unodeque
append
ypopleft
da como resultado resultados mucho peoresdeque
que la mejora 20x lograda por eldeque
comportamiento predeterminado :@Jonathan modifica un poco su código y obtengo el punto de referencia usando cPython 3.6.2 y agrego la condición en el bucle deque para simular el comportamiento de Queue do.
Y parece que el rendimiento está limitado por esta función
condition.notify_all()
fuente
deque
es seguro para subprocesos. "operaciones que no requieren bloqueo" significa que no tiene que hacer el bloqueo usted mismo,deque
se encarga de ello.Echando un vistazo a la
Queue
fuente, se llama a la deque internaself.queue
y utiliza un mutex para accesores y mutaciones, porQueue().queue
lo que no es seguro para usar en subprocesos.Si está buscando un operador "in", una deque o cola posiblemente no sea la estructura de datos más adecuada para su problema.
fuente
(parece que no tengo reputación para comentar ...) Debes tener cuidado con los métodos de deque que utilizas de diferentes hilos.
deque.get () parece ser seguro para subprocesos, pero he encontrado que hacer
puede fallar si otro hilo está agregando elementos al mismo tiempo. Obtuve una RuntimeException que se quejaba de "mutar deque durante la iteración".
Consulte collectionsmodule.c para ver qué operaciones se ven afectadas por esto
fuente
>>> di = {1:None} >>> for x in di: del di[x]
while
bucle.