Многопроцессорная обработка Python и доступ к базе данных с помощью pyodbc "не является безопасным"? - PullRequest
10 голосов
/ 08 октября 2009

Проблема:

Я получаю следующую трассировку и не понимаю, что это значит или как это исправить:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Python26\lib\multiprocessing\forking.py", line 342, in main
    self = load(from_parent)
  File "C:\Python26\lib\pickle.py", line 1370, in load
    return Unpickler(file).load()
  File "C:\Python26\lib\pickle.py", line 858, in load
    dispatch[key](self)
  File "C:\Python26\lib\pickle.py", line 1083, in load_newobj
    obj = cls.__new__(cls, *args)
TypeError: object.__new__(pyodbc.Cursor) is not safe, use pyodbc.Cursor.__new__()

Ситуация:

У меня есть база данных SQL Server, заполненная данными для обработки. Я пытаюсь использовать многопроцессорный модуль для распараллеливания работы и использования преимуществ нескольких ядер на моем компьютере. Моя общая структура классов выглядит следующим образом:

  • MyManagerClass
    • Это основной класс, с которого начинается программа.
    • Создает два объекта многопроцессорной обработки. Один work_queue и один write_queue
    • Он также создает и запускает другие процессы, а затем ожидает их завершения.
    • ПРИМЕЧАНИЕ: это , а не расширение многопроцессорной обработки. Менеджеры. BaseManager ()
  • MyReaderClass
    • Этот класс считывает данные из базы данных SQL Server.
    • Это помещает предметы в work_queue.
  • MyWorkerClass
    • Здесь происходит обработка работы.
    • Он получает предметы из work_queue и кладет завершенные предметы в write_queue.
  • MyWriterClass
    • Этот класс отвечает за запись обработанных данных обратно в базу данных SQL Server.
    • Получает предметы от write_queue.

Идея состоит в том, что будет один менеджер, один читатель, один писатель и много работников.

Другие детали:

Я получаю трассировку дважды в stderr, так что я думаю, что это происходит один раз для читателя и один раз для автора. Мои рабочие процессы создаются нормально, но просто сидите там, пока я не отправлю KeyboardInterrupt, потому что они ничего не имеют в work_queue.

И читатель, и писатель имеют собственное подключение к базе данных, созданной при инициализации.

Решение:

Спасибо Марку и Фердинанду Бейеру за их ответы и вопросы, которые привели к этому решению. Они справедливо отметили, что объект Cursor не является «способным к расслаиванию», что является методом, который многопроцессорный использует для передачи информации между процессами.

Проблема с моим кодом заключалась в том, что MyReaderClass(multiprocessing.Process) и MyWriterClass(multiprocessing.Process) оба подключались к базе данных в своих __init__() методах. Я создал оба этих объекта (то есть вызвал их метод init) в MyManagerClass, затем назвал start().

Таким образом, он создает объекты соединения и курсора, а затем пытается отправить их в дочерний процесс с помощью pickle. Моим решением было перенести создание экземпляров объектов подключения и курсора в метод run (), который не вызывается до тех пор, пока дочерний процесс не будет полностью создан.

Ответы [ 3 ]

10 голосов
/ 08 октября 2009

Многопроцессорность основывается на травлении для связи объектов между процессами. Соединение pyodbc и объекты курсора не могут быть засечены.

>>> cPickle.dumps(aCursor)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.5/copy_reg.py", line 69, in _reduce_ex
    raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle Cursor objects
>>> cPickle.dumps(dbHandle)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.5/copy_reg.py", line 69, in _reduce_ex
    raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle Connection objects

"Это помещает элементы в work_queue", какие элементы? Возможно ли, что объект-курсор также передается?

3 голосов
/ 08 октября 2009

Ошибка возникает в модуле pickle, поэтому где-то ваш объект DB-Cursor подвергается засечке и отсечению (сериализуется в хранилище и снова не сериализуется в объект Python).

Полагаю, что pyodbc.Cursor не поддерживает травление. Почему вы все равно должны пытаться сохранить объект-курсор?

Проверьте, используете ли вы pickle где-то в вашей цепочке работы или оно используется неявно.

1 голос
/ 30 октября 2014

pyodbc имеет Python DB-API уровень безопасности потоков 1 . Это означает, что потоки не могут совместно использовать соединения, и это вообще не безопасно.

Я не думаю, что основанные на потоке безопасные драйверы ODBC имеют значение. Это в коде Python, как отмечено ошибкой Pickling.

...