cx_Oracle, generadores, y los hilos en Python
-
22-09-2019 - |
Pregunta
¿Cuál es el comportamiento de los cursores cx_Oracle cuando el objeto de conexión es utilizado por diferentes hilos? ¿Cómo afectará a los generadores de este comportamiento? Específicamente ...
Editar : La función original ejemplo era incorrecta; un generador estaba siendo devuelto por una función sub, yield
no se utilizó directamente en el bucle. Esto clarifica cuando se ejecuta finally
(después de return
hace), pero todavía no responde si un cursor se puede utilizar si otro hilo comienza a utilizar el objeto de conexión el cursor se creó a partir. En realidad, parece (en Python 2.4, por lo menos), try...finally
con yield
provoca un error de sintaxis.
def Get()
conn = pool.get()
try:
cursor = conn.cursor()
cursor.execute("select * from table ...")
return IterRows(cursor)
finally:
pool.put(conn)
def IterRows(cursor):
for r in cursor:
yield r
Get()
es una función llamada por múltiples hilos. Las conexiones se crean con el argumento threaded=False
.
Me pregunto ...
- ¿Es el hilo de 1 objeto
cursor
usables si rosca 2 llega y utiliza el mismo objeto de conexión? Si no es así, lo que podría suceder?
El comportamiento que estoy viendo es una excepción en cx_Oracle hablando de un error de protocolo, y luego una violación de segmento siguiente.
Solución
los documentos : threadsafety
es, y cito,
Actualmente 2, lo que significa que los hilos pueden compartir el módulo y conexiones, pero no los cursores.
Por lo que su construcción "pool de cursores" (donde un cursor puede ser utilizado por diferentes hilos) parece estar más allá del nivel threadsafety
. No es una cuestión de compartir conexiones (eso está bien ya que he pasado threaded
adecuadamente en el constructor de la conexión), pero los cursores. Es posible que desee almacenar cada cursor en threading.local
después de la primera vez que un mensaje ha utilizado, de manera que cada hilo puede tener su propio 1-cursor del "pool" (no una optimización clave, sin embargo: hacer un nuevo cursor no es un pesado operación de trabajo).
Wrt pregunta 2, se ejecuta la cláusula finally
cuando el objeto generador (construido por una llamada a la función Get
generador) se hace todo - ya sea porque es elevar StopIteration
, o porque está siendo recogido de basura (por lo general debido a que la última referencia a ella simplemente ha desaparecido). Por ejemplo, si la persona que llama es:
def imthecaller():
for i, row in enumerate(Get()):
print i, row
if i > 1: break
# this is the moment the generators' finally-clause runs
print 'bye'
se ejecuta el finally
después (como máximo) 3 filas han sido yield
ed.