Python中的cx_Oracle、生成器和线程

5 投票
1 回答
3515 浏览
提问于 2025-04-15 19:33

当不同的线程使用同一个连接对象时,cx_Oracle 的游标会有什么表现呢?生成器又会如何影响这种表现呢?具体来说……

编辑:原来的示例函数有误;一个子函数返回了生成器,但在循环中并没有直接使用 yield。这澄清了 finally 是在 return 之后执行的,但仍然没有回答如果另一个线程开始使用创建游标的连接对象,游标是否还能用。实际上(至少在 Python 2.4 中),try...finallyyield 一起使用会导致语法错误。

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 参数创建的。

我在想……

  1. 如果线程 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后执行。

撰写回答