2011-03-24 13 views
5

Digamos que tengo una función que escribe en un archivo. También tengo una función que itera repetidamente leyendo desde dicho archivo. Tengo ambas funciones ejecutándose en hilos separados. (En realidad estoy leyendo/escribiendo en registros a través de MDIO, por lo que no puedo tener ambos hilos ejecutándose simultáneamente, solo uno o el otro, pero por simplicidad, digamos que es un archivo)Python: threading + lock ralentiza mi aplicación considerablemente

Ahora cuando Ejecuto la función de escritura de forma aislada, se ejecuta bastante rápido. Sin embargo, cuando estoy ejecutando un enhebrado y obtengo un bloqueo antes de ejecutarlo, parece que funciona extremadamente lento. ¿Esto es porque el segundo hilo (función de lectura) está sondeando para adquirir el bloqueo? ¿Hay alguna forma de evitar esto?

Actualmente estoy usando un RLock simple, pero estoy abierto a cualquier cambio que aumente el rendimiento.

Editar: Como ejemplo, voy a poner un ejemplo básico de lo que está sucediendo. El hilo de lectura básicamente se está ejecutando, pero de vez en cuando un hilo separado hará una llamada para cargar. Si comparo ejecutando load desde cmd prompt, ejecutar en un thread es al menos 3 veces más lento.

hilo de escritura:

import usbmpC# functions I made which access dll functions for hardware, etc 

def load(self, lock): 
    lock.acquire() 
    f = open('file.txt','r') 
    data = f.readlines() 
    for x in data: 
     usbmpc.write(x) 
    lock.release() 

lectura hilo:

import usbmpc 

def read(self, lock): 
    addr = START_ADDR 
    while True: 
     lock.acquire() 
     data = usbmpc.read(addr) 
     lock.release() 
     addr += 4 
     if addr > BUF_SIZE: addr = START_ADDR 
+0

En CPython, excluyendo el módulo C (externo) liberador de GIL, solo hay un hilo que se "ejecuta" a la vez ya que solo un hilo tiene permiso para acceder al motor Python a la vez. Si la llamada MDIO no libera el GIL, la otra función/bloqueo * ni siquiera puede iniciarse * (es decir, el código Python no se ejecutará) hasta que se complete la llamada MDIO. (No conozco el modo MDIO que tiene hilos). –

+0

Edité la publicación con un ejemplo. ¿Estás diciendo que una vez que el hilo de escritura adquiere el bloqueo, el hilo de lectura nunca se ejecutará? Estaba bajo la cerradura de la impresión. ¿Comprará la encuesta hasta que adquiera la cerradura? ¿Qué otra cosa podría estar ralentizando el código anterior? –

+0

@Shaunak Amin No estaba tratando de decir/insinuar que :-) Pero el enhebrado CPython no puede ejecutar múltiples hilos de Python al mismo tiempo (se enclavan internamente en el GIL, solo algunas instrucciones permiten que un hilo de Python "ceda" ") - pero esto no se aplica realmente con la actualización anterior ya que los bloqueos son sobre el acceso usbmpc. –

Respuesta

4

Cómo se utiliza el roscado en una máquina de múltiples núcleos?

Si la respuesta es sí, entonces, a menos que su versión Python sea 3.2+, sufrirá un rendimiento reducido al ejecutar aplicaciones con subprocesos.

David Beazly ha puesto un esfuerzo considerable para encontrar lo que está pasando con GIL en multinúcleos y ha hecho que sea fácil para el resto de nosotros entenderlo también. Consulte su sitio web y los recursos allí. También es posible que desee ver su presentation en PyCon 2010. Es bastante interesante.

Para resumir, en Python 3.2, Antoine Pitrou wrote a new GIL que tiene el mismo rendimiento en máquinas individuales y multinúcleo. En versiones anteriores, los más núcleos/hilos que tenga, la pérdida de rendimiento aumenta ...

esperan que ayude :)

+0

Ambas excelentes respuestas, pero estos enlaces me dieron algo bueno para seguir. Ya había decidido antes de cada respuesta para eliminar el enhebrado y manejar la programación yo mismo (para evitar el cambio de contexto perjudicando el rendimiento), ya que mis necesidades son bastante primitivas. Sin embargo, me gustaría poder marcar ambas respuestas. –

+2

Desde entonces he codificado mi propio programador para evitar el cambio de contexto GIL entre subprocesos, y una función que anteriormente tomaba más de 10 minutos ahora toma 20 segundos (debido a la eliminación de varios subprocesos que compiten por ciclos). ESTA ES UNA ADVERTENCIA: NO USE ROSCADO EN PYTHON 2.x !!! –

+0

@ShaunakAmin: Maldición. –

4

Por qué no se adquiere el bloqueo en el escritor durante la duración de cada uno sólo escribir ? Actualmente está bloqueando durante toda la duración de la función de carga, el lector nunca entra hasta que la función de carga esté completamente hecha.

En segundo lugar, debe utilizar bloqueos de contexto. Su código actual no es seguro para subprocesos:

def load(lock): 
    for x in data: 
     with lock: 
      whatever.write(x) 

Lo mismo aplica para su lector. Use un contexto para mantener el bloqueo.

En tercer lugar, no utilice un RLock. Usted sabe que no necesita uno, en ningún momento necesita readquirir su código de lectura/escritura, así que no le dé esa oportunidad, estará enmascarando errores.

La respuesta real está en varios de los comentarios a su pregunta: El GIL está causando cierta controversia (suponiendo que no sea realmente su mal uso del bloqueo). El módulo Python threading es fantástico, el GIL a veces no lo es, pero más aún, los comportamientos complejos que genera son incomprendidos.Sin embargo, vale la pena mencionar que la creencia generalizada de que lanzar hilos a los problemas no es la panacea que la gente cree que es. Por lo general, no es la solución.