metodos full asyncio python thread-safety queue

full - queue python 3



Queue.Queue vs. collections.deque (7)

(Parece que no tengo reputación para comentar ...) Debe tener cuidado con los métodos del deque que usa de diferentes hilos.

deque.get () parece ser seguro para el hilo, pero he encontrado que hacerlo

for item in a_deque: process(item)

puede fallar si otro hilo está agregando elementos al mismo tiempo. Obtuve una RuntimeException que se quejó "deque mutado durante la iteración".

Compruebe collectionsmodule.c para ver qué operaciones se ven afectadas por este

Necesito una cola en la que varios subprocesos puedan insertar cosas y de la que puedan leerse varios subprocesos.

Python tiene al menos dos clases de cola, Queue.Queue y collections.deque, y la primera aparentemente usa la última internamente. Ambos afirman ser seguros para subprocesos en la documentación.

Sin embargo, los documentos de Queue también indican:

collections.deque es una implementación alternativa de colas ilimitadas con operaciones rápidas atómicas append () y popleft () que no requieren bloqueo.

Lo cual creo que no entiendo del todo: ¿Esto significa 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. Veo que Queue agrega funcionalidad de bloqueo. Por otro lado, pierde algunas características de deque como soporte para el operador interno.

Acceder directamente al objeto deque interno, 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?


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 deque son seguros para subprocesos"

En pocas palabras aquí: https://bugs.python.org/issue15329#msg199368

Las operaciones append (), appendleft (), pop (), popleft () y len (d) de deque son seguras para subprocesos en CPython. Los métodos de adición tienen un DECREF al final (para los casos donde se ha establecido maxlen), pero esto sucede después de que se hayan realizado todas las actualizaciones de la estructura y se hayan restaurado las invariantes, por lo que está bien tratar estas operaciones como atómicas.

De todos modos, si no está 100% seguro y prefiere la confiabilidad sobre el rendimiento, simplemente coloque un bloqueo similar;)


Si todo lo que está buscando es una forma segura de transferir objetos entre subprocesos , ambos funcionarían (tanto para FIFO como para LIFO). Para FIFO:

Nota:

  • Es posible que otras operaciones en deque no sean seguras para hilos, no estoy seguro.
  • deque no bloquea en pop() o popleft() por lo que no puede basar el flujo de hilos del consumidor en el bloqueo hasta que llegue un nuevo elemento.

Sin embargo, parece que deque tiene una ventaja de eficiencia significativa . Aquí hay algunos resultados de referencia en segundos usando CPython 2.7.3 para insertar y eliminar 100k elementos

deque 0.0747888759791 Queue 1.60079066852

Aquí está el código de referencia:

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


Todos los métodos de un solo elemento en deque son atómicos y seguros para subprocesos. Todos los demás métodos también son seguros para subprocesos. Las cosas como len(dq) , dq[4] arrojan valores correctos momentáneos. Pero piense, por ejemplo, sobre dq.extend(mylist) : no se garantiza que todos los elementos en mylist se mylist en una fila cuando otros hilos también agregan elementos en el mismo lado, pero eso no es un requisito en la comunicación entre hilos. y para la tarea cuestionada.

Así que un deque es ~ 20 veces más rápido que Queue (que usa un deque debajo del capó) y, a menos que no necesite la API de sincronización "cómoda" (bloqueo / tiempo de espera), la maxsize máxima de maxsize o "Anular estos métodos (_put, _get, ..) para implementar el comportamiento de subclasificación de otras organizaciones de cola , o cuando usted mismo se encarga de tales cosas, entonces un deque es una buena y eficiente oferta para la comunicación entre hilos de alta velocidad.

De hecho, el uso intensivo de un método extra mutex y método adicional ._get() etc., las llamadas al método en Queue.py obedecen a restricciones de compatibilidad con versiones anteriores, el exceso de diseño pasado y la falta de cuidado para proporcionar una solución eficiente para este importante problema de cuello de botella de velocidad en la comunicación entre hilos. Se utilizó una lista en versiones anteriores de Python, pero incluso list.append () /. Pop (0) era & is atomic y threadsafe ...


Queue.Queue y collections.deque sirven para diferentes propósitos. Queue.Queue está diseñado para permitir que diferentes hilos se comuniquen utilizando mensajes / datos en cola, mientras que collections.deque simplemente está diseñado como una estructura de datos. Es por eso que Queue.Queue tiene métodos como put_nowait() , get_nowait() y join() , mientras que collections.deque no lo hace. Queue.Queue no está destinado a ser utilizado como una colección, por lo que carece de los gustos del operador in .

Todo se reduce a esto: si tienes múltiples hilos y quieres que se puedan comunicar sin la necesidad de bloqueos, estás buscando Queue.Queue ; si solo desea una cola o una cola de doble extremo como una estructura de datos, use collections.deque .

Finalmente, acceder y manipular el deque interno de un Queue.Queue es jugar con fuego, realmente no quieres estar haciendo eso.


deque es seguro para subprocesos. "operaciones que no requieren bloqueo" significa que usted no tiene que hacer el bloqueo usted mismo, el deque se ocupa de ello.

self.queue el origen de Queue , el deque interno se llama self.queue y usa un mutex para accessors y mutaciones, por lo que Queue().queue no es seguro para la ejecución de subprocesos.

Si está buscando un operador "in", entonces una deque o cola posiblemente no sea la estructura de datos más apropiada para su problema.


deque 0.469802 Queue 0.667279

@Jonathan modifica su código un poco y obtengo el punto de referencia usando cPython 3.6.2 y agrego la condición en deque loop para simular el comportamiento Queue do.

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)

Y parece que el rendimiento está limitado por esta función condition.notify_all()

collections.deque es una implementación alternativa de colas ilimitadas con operaciones rápidas atómicas append () y popleft () que no requieren bloqueo. docs Cola