cx_Oracle, генераторы и потоки в Python
-
22-09-2019 - |
Вопрос
Каково поведение курсоров cx_Oracle, когда объект connection используется разными потоками?Как генераторы повлияют на это поведение?В частности...
Редактировать:Исходный пример функции был некорректным;генератор возвращался подфункцией, 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
cursor
объект все еще можно использовать, если появится поток 2 и использует тот же объект подключения?Если нет, то что может произойти?
Поведение, которое я вижу, - это исключение в cx_Oracle, говорящее об ошибке протокола, а затем следует segfault .
Решение
Видишь документы: threadsafety
есть, и я цитирую,
В настоящее время 2, что означает, что потоки могут совместно использовать модуль и соединения, но не курсоры.
Таким образом, ваша конструкция "пула курсоров" (где один курсор может использоваться разными потоками), похоже, выходит за рамки threadsafety
Уровень.Это не проблема совместного использования подключений (это нормально, поскольку вы прошли threaded
правильно в конструкторе соединения), но курсоры.Возможно, вы захотите сохранить каждый курсор в threading.local
после того, как поток использовал его в первый раз, так что каждый поток может иметь свой собственный "пул" с одним курсором (хотя это и не ключевая оптимизация:создание нового курсора не является трудоемкой операцией).
Wrt ваш вопрос 2, the 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
эд.