cx_Oracle, generatori e thread in Python
-
22-09-2019 - |
Domanda
Qual è il comportamento di cursori cx_Oracle quando l'oggetto di connessione viene utilizzato da diversi thread? Come potrebbero influenzare generatori di questo comportamento? In particolare ...
Modifica : La funzione di esempio originale non era corretta; un generatore veniva restituito da una funzione secondaria, yield
non è stato utilizzato direttamente nel ciclo. Questo chiarisce quando viene eseguita finally
(dopo return
fa), ma ancora non risponde se un cursore può essere utilizzato se un altro thread inizia a utilizzare la connessione oggetto il cursore è stato realizzato. In realtà sembra (in python 2.4, almeno), try...finally
con yield
causa un errore di sintassi.
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()
è una funzione chiamata da più thread. I collegamenti vengono creati con l'argomento threaded=False
.
Mi chiedo ...
- è thread 1 dell'oggetto
cursor
ancora utilizzabile se il thread 2 arriva e utilizza lo stesso oggetto di connessione? Se no, cosa potrebbe accadere?
Il comportamento che sto vedendo è un'eccezione in cx_Oracle parlando di un errore di protocollo, e poi un segfault segue.
Soluzione
la documentazione : threadsafety
è, e cito,
Attualmente 2, il che significa che le discussioni può condividere il modulo e le connessioni, ma non i cursori.
Così il vostro "pool di cursori" costruire (dove un cursore può essere utilizzato da diversi thread) sembra essere oltre il livello threadsafety
. Non è una questione di condivisione delle connessioni (che è OK dal momento che hai passato threaded
correttamente nel costruttore della connessione), ma i cursori. Si consiglia di memorizzare ogni cursore nella threading.local
dopo la prima volta che un thread è usato, in modo che ogni thread può avere il proprio 1-cursor "pool" (non un'ottimizzazione chiave, però: fare un nuovo cursore non è un heavy- funzionamento dovere).
Wrt tua domanda 2, la clausola finally
eseguito quando l'oggetto generatore (costruita da una chiamata alla funzione generatore Get
) è tutto fatto - sia perché si tratta di sollevare StopIteration
, o perché è in fase di garbage collection (in genere perché l'ultimo di riferimento ad esso è appena andato via). Per esempio se il chiamante è:
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'
il finally
eseguito dopo (al massimo) 3 righe sono state yield
ed.