Python中的cx_Oracle、生成器和线程
当不同的线程使用同一个连接对象时,cx_Oracle 的游标会有什么表现呢?生成器又会如何影响这种表现呢?具体来说……
编辑:原来的示例函数有误;一个子函数返回了生成器,但在循环中并没有直接使用 yield
。这澄清了 finally
是在 return
之后执行的,但仍然没有回答如果另一个线程开始使用创建游标的连接对象,游标是否还能用。实际上(至少在 Python 2.4 中),try...finally
和 yield
一起使用会导致语法错误。
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()
是一个被多个线程调用的函数。连接是通过设置 threaded=False
参数创建的。
我在想……
- 如果线程 2 过来使用同一个连接对象,线程 1 的
cursor
对象还能用吗?如果不能,会发生什么?
我看到的情况是 cx_Oracle 报告了一个协议错误,然后接着出现了段错误(segfault)。
1 个回答
2
请查看文档:threadsafety
的意思是,
目前是2,这意味着线程可以共享模块和连接,但不能共享游标。
所以你提到的“游标池”构造(一个游标可以被不同的线程使用)似乎超出了threadsafety
的范围。共享连接是没问题的(因为你在连接的构造函数中正确地使用了threaded
),但游标就不行了。你可能想在一个线程第一次使用游标后,把每个游标存储在threading.local
中,这样每个线程就可以有自己的一个游标“池”(不过这并不是一个关键的优化:创建一个新游标并不是一个重负的操作)。
关于你问的第二个问题,finally
语句块会在生成器对象(通过调用你的生成器函数Get
创建的)完成时执行——这可能是因为它抛出了StopIteration
,或者因为它被垃圾回收(通常是因为最后一个引用消失了)。例如,如果调用者是:
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'
那么finally
会在最多3行被yield
后执行。