2009-10-08 1 views
10

Проблема: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.

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

Решение:

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

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

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

+0

Просто сказать: отличный вопрос. – mavnn

ответ

8

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

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

+0

У меня есть генератор, который перебирает элементы в курсоре (в основном вызывает pyodbyc.Cursor(). Fetchone()). Я считаю, что он дает кортеж (id, stuff_to_process), который я вставляю в очередь. Я попытался сделать глубокую копию, но это не сработало. Я посмотрел на помощь, и на самом деле это экземпляр объекта Row. Поэтому мне может потребоваться сначала преобразовать в кортеж. – tgray

+0

Объект Row должен содержать ссылку на Курсор или что-то в этом роде. – tgray

+0

Преобразование строки в кортеж не решило ее. – tgray

3

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

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

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

+0

Похоже, что многопроцессорность использует это неявно для передачи вещей через объекты Pipe между процессами (в частности, объекты Queue, которые я создал). – tgray

1

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

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