cx_Oracle, генераторы и потоки в Python - PullRequest
4 голосов
/ 22 февраля 2010

Каково поведение курсоров 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.

Мне интересно ...

  1. Является ли объект потока 1 cursor все еще пригодным для использования, если поток 2 появляется и использует тот же объект соединения? Если нет, что может произойти?

Поведение, которое я наблюдаю, является исключением в cx_Oracle, говорящем об ошибке протокола, и затем следует segfault.

1 Ответ

2 голосов
/ 22 февраля 2010

См. документы : threadsafety есть, и я цитирую,

В настоящее время 2, что означает, что темы может поделиться модулем и подключениями, но не курсоры.

Таким образом, ваша конструкция «пул курсоров» (где один курсор может использоваться разными потоками), кажется, выходит за уровень threadsafety. Это не проблема совместного использования соединений (это нормально, поскольку вы правильно указали threaded в конструкторе соединения), а курсоры. Возможно, вы захотите сохранить каждый курсор в threading.local после первого использования потока, чтобы каждый поток мог иметь свой собственный «пул» с 1 курсором (хотя это не ключевая оптимизация: создание нового курсора не является сверхмощная операция).

По вашему вопросу 2 предложение 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 ed.

...