Многопроцессорность Python и доступ к базе данных с помощью pyodbc "небезопасны"?

Проблема:

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

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

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

Другие сведения:

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

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

Решение:

Спасибо Mark и Ferdinand Beyer за их ответы и вопросы, которые привели к этому решению. Они по праву отметили, что объект Cursor не "pickle-able", который является методом, который использует многопроцессорность для передачи информации между процессами.

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

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

Ответ 1

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

>>> 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", какие предметы? Возможно ли, что объект курсора тоже пройдет?

Ответ 2

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

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

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

Ответ 3

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

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