cx_Oracle, Generatoren und Fäden in Python
-
22-09-2019 - |
Frage
Was ist das Verhalten von cx_Oracle Cursor, wenn das Verbindungsobjekt von verschiedenen Threads verwendet wird? Wie würden Generatoren beeinflussen dieses Verhalten? Insbesondere ...
Bearbeiten : Die ursprüngliche Beispiel Funktion war falsch; ein Generator, der durch eine Unterfunktion zurückgegeben wurde, wurde yield
nicht direkt in der Schleife verwendet. Damit wird klar gestellt, wenn finally
ausgeführt wird (nach return
der Fall ist), aber nicht beantwortet immer noch nicht, ob ein Cursor verwendet werden kann, wenn ein anderer Thread das Verbindungsobjekt beginnt mit dem Cursor aus erstellt wurde. Es scheint tatsächlich (in Python 2.4, zumindest), try...finally
mit yield
verursacht einen Syntaxfehler.
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()
ist eine Funktion von mehreren Threads aufgerufen. Die Verbindungen werden mit dem Argument threaded=False
erstellt.
Ich frage mich ...
- Ist Gewinde 1 der
cursor
Objekt noch verwendbar, wenn Gewinde 2 kommen und verwenden das gleiche Verbindungsobjekt? Wenn nicht, was passieren könnte?
Das Verhalten i sehen bin eine Ausnahme in cx_Oracle spricht von einem Protokollfehler, und dann ein segfault folgt.
Lösung
Siehe die docs : threadsafety
ist, und ich zitiere:
Zur Zeit 2, was bedeutet, dass Fäden kann das Modul und Verbindungen teilen, aber nicht Cursor.
So Ihr „Pool von Cursor“ construct (wo ein Cursor durch verschiedene Threads verwendet werden kann) scheint über das threadsafety
Niveau. Es ist nicht eine Frage des Teilens Verbindungen aber Cursor (das ist in Ordnung, da Sie threaded
richtig in die Verbindung des Konstruktor übergeben haben). Sie können jeden Cursor in threading.local
nach dem ersten Mal speichern möchten ein Thread es verwendet hat, so dass jeder Thread kann seine eigenen 1-Cursor „Pool“ (keine Schlüssel Optimierung haben, aber: einen neuen Cursor zu machen ist kein Schwer- Betriebsbelastu).
Wrt Ihre Frage 2, die finally
Klausel ausgeführt wird, wenn der Generator Objekt (gebaut von einem Aufruf an Ihrer Generatorfunktion Get
) ist alles erledigt - entweder weil es StopIteration
erhöhen, oder weil es Müll gesammelt hat wird (in der Regel, weil die letzte Referenz um es einfach weg weg) hat. Z. B, wenn der Anrufer ist:
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'
die finally
aus, nachdem (höchstens) 3 Reihen haben yield
ed worden.