2009-10-08 21 views
10

問題:pyodbcによるPythonマルチプロセッシングとデータベースアクセスは "安全ではありません"?

私は、次のトレースバックを取得していますし、それが何を意味するのか理解していないか、それを修正する方法:

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
    • これは、メインクラス、プログラムが開始されます。
    • それらが終了するため、それはまた、他のプロセスを作成し、起動する2つのmultiprocessing.Queueオブジェクト一つwork_queuewrite_queue
    • を作成し、その後、待機します。
    • 注:これはない multiprocessing.managers.BaseManagerの拡張子(ある)
  • MyReaderClass
    • このクラスは、SQL Serverデータベースからデータを読み込みます。
    • work_queueに項目を入れます。
  • MyWorkerClass
    • 業務処理が起こる場所です。
    • work_queueからアイテムを取得し、完了したアイテムをwrite_queueに配置します。
  • MyWriterClass
    • このクラスは、バックSQL Serverデータベースに処理されたデータの書き込みが担当しています。
    • write_queueからアイテムを取得します。

アイデアは1人のマネージャー、ある読者、1人のライター、そして多くの労働者があるだろうということです。

その他の詳細:

私は標準エラー出力に二回トレースバックを取得するので、私はそれは作家のために一度、読者のために一度起こるとすることを考えています。私のワーカープロセスはうまく作成されますが、work_queueに何もないので、KeyboardInterruptを送信するまでそこに座ってください。

リーダーとライターの両方が、データベースとの独自の接続を持ち、初期化時に作成されます。

ソリューション:このソリューションにつながっその答えと質問のためのマークとフェルディナント・バイエルに

感謝。彼らは正当にCursorオブジェクトが "pickle-able"ではないことを指摘しました。これは、マルチプロセスがプロセス間で情報を渡すために使用する方法です。

私のコードの問題は、__init__()の方法でMyReaderClass(multiprocessing.Process)MyWriterClass(multiprocessing.Process)の両方がデータベースに接続されていたことです。 MyManagerClassにこれらのオブジェクト(つまりinitメソッドと呼ばれる)を作成した後、start()というオブジェクトを作成しました。

したがって、接続オブジェクトとカーソルオブジェクトが作成され、次にpickle経由で子プロセスに送信されます。私の解決策は、接続とカーソルオブジェクトのインスタンス化を子プロセスが完全に作成されるまで呼び出されないrun()メソッドに移すことでした。

+0

ちょうどいいこと:優秀な質問。 – mavnn

答えて

8

マルチプロセッシングは、プロセス間でオブジェクトを通信するための酸洗いに依存しています。 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にアイテムを入れます」という項目は何ですか?カーソルオブジェクトも渡すことが可能ですか?

+0

私はカーソル内の項目をループするジェネレータを持っています(基本的にはpyodbyc.Cursor()。fetchone()を呼び出します)。私はそれがキューに入れたものである(id、stuff_to_process)のタプルを生成すると信じています。私はディープコピーを作ってみたが、うまくいかなかった。私はヘルプを見て、それは実際に行オブジェクトのインスタンスです。ですから、最初にタプルに変換する必要があるかもしれません。 – tgray

+0

Rowオブジェクトには、Cursorなどへの参照が含まれている必要があります。 – tgray

+0

行をタプルに変換しても解決できませんでした。 – tgray

3

pickleモジュール内でエラーが発生します。そのため、DB-Cursorオブジェクトがpickle化され、unpickleされます(ストレージにシリアル化され、Pythonオブジェクトに再度シリアル化されません)。

pyodbc.Cursorは酸洗をサポートしていないと思います。とにかくカーソルオブジェクトを永続化しようとするべきですか?

pickleをワークチェーンのどこかに使用するかどうか、または暗黙的に使用されているかどうかを確認してください。

+0

マルチプロセスのように見えるのは、プロセス間のPipeオブジェクト(具体的には作成したQueueオブジェクト)を暗黙的に渡すためです。 – tgray

1

pyodbcにはPython DB-API threadsafety level 1があります。つまり、スレッドは接続を共有できず、スレッドセーフではありません。

私は基本的なスレッドセーフなODBCドライバが違いをもたらすとは思わない。 Picklingエラーで指摘されているPythonコードです。

関連する問題