cx_oracle, geradores e tópicos em python
-
22-09-2019 - |
Pergunta
Qual é o comportamento dos cursores cx_oracle quando o objeto de conexão é usado por threads diferentes? Como os geradores afetariam esse comportamento? Especificamente...
Editar: A função de exemplo original estava incorreta; Um gerador estava sendo devolvido por uma sub -função, yield
não foi usado diretamente no loop. Isso esclarece quando finally
é executado (depois return
faz), mas ainda não responde se um cursor pode ser usado se outro thread começar a usar o objeto de conexão de onde o cursor foi criado. Na verdade, parece (no Python 2.4, pelo menos), try...finally
com yield
causa um erro de sintaxe.
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()
é uma função chamada por vários threads. As conexões são criadas com o threaded=False
argumento.
Estou me perguntando ...
- É o thread 1
cursor
Objeto ainda utilizável se o Thread 2 aparecer e usar o mesmo objeto de conexão? Caso contrário, o que pode acontecer?
O comportamento que estou vendo é uma exceção no CX_oracle falando sobre um erro de protocolo e, em seguida, segue -se um segfault.
Solução
Ver os documentos: threadsafety
é, e eu cito,
Atualmente 2, o que significa que os threads podem compartilhar o módulo e as conexões, mas não os cursores.
Portanto, sua construção "pool de cursores" (onde um cursor pode ser usado por diferentes threads) parece estar além do threadsafety
nível. Não é uma questão de compartilhar conexões (tudo bem desde que você passou threaded
corretamente no construtor da conexão), mas cursores. Você pode querer armazenar cada cursor em threading.local
Após a primeira vez, um thread o usou, para que cada thread possa ter seu próprio "pool" de 1 cursor (porém, não é uma otimização de chave: fazer um novo cursor não é uma operação pesada).
Wrt sua pergunta 2, o finally
A cláusula é executada quando o objeto do gerador (construído por uma chamada para a função do seu gerador Get
) está tudo pronto - porque está criando StopIteration
, ou porque está sendo coletado com lixo (normalmente porque a última referência a ele acabou de desaparecer). Por exemplo, se o chamador for:
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'
a finally
executa após (no máximo) 3 linhas foram yield
ed.