0

これを説明したり、多くのコードを示したりするのは難しいですが、やってみます。基本的に、スレッド内のテーブルの追加/削除/変更を頻繁に処理するマルチスレッド デスクトップ アプリがあります。私が読んだことから、scoped_session を使用し、それをさまざまなスレッドに渡して作業を行う必要があります (と思いますか?)。基本的なコード例を次に示します。

class SQL():
    def __init__(self):        
        self.db = create_engine('mysql+mysqldb://thesqlserver')
        self.metadata = MetaData(self.db)
        self.SessionObj = scoped_session(sessionmaker(bind=self.db, autoflush=True))

db = SQL()
session = db.SessionObj()
someObj = Obj(val, val2)
session.add(someObj)
session.commit()

上記のクラスは、私が SQL の一般的なアクセスとして使用しているものです。新しいセッションを作成し、クエリを実行して更新/追加した後、session.commit() で次のエラーが発生します。

Traceback (most recent call last):
  File "core\taskHandler.pyc", line 42, in run
  File "core\taskHandler.pyc", line 184, in addTasks
  File "core\sqlHandler.pyc", line 35, in commit
  File "sqlalchemy\orm\session.pyc", line 624, in rollback
  File "sqlalchemy\orm\session.pyc", line 338, in rollback
  File "sqlalchemy\orm\session.pyc", line 369, in _rollback_impl
  File "sqlalchemy\orm\session.pyc", line 239, in _restore_snapshot
  File "sqlalchemy\orm\state.pyc", line 252, in expire
AttributeError: 'NoneType' object has no attribute 'expire'

次に、別のSQL試行が行われた場合:

Traceback (most recent call last):
  File "core\taskHandler.pyc", line 44, in run
  File "core\taskHandler.pyc", line 196, in deleteTasks
  File "sqlalchemy\orm\query.pyc", line 2164, in scalar
  File "sqlalchemy\orm\query.pyc", line 2133, in one
  File "sqlalchemy\orm\query.pyc", line 2176, in __iter__
  File "sqlalchemy\orm\query.pyc", line 2189, in _execute_and_instances
  File "sqlalchemy\orm\query.pyc", line 2180, in _connection_from_session
  File "sqlalchemy\orm\session.pyc", line 729, in connection
  File "sqlalchemy\orm\session.pyc", line 733, in _connection_for_bind
  File "sqlalchemy\orm\session.pyc", line 249, in _connection_for_bind
  File "sqlalchemy\orm\session.pyc", line 177, in _assert_is_active
sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back by a nested rollback() call.  To begin a new transaction, issue Session.rollback() first.

それは私が知っている限りであり、私が説明できる最善のものだと思います. 私がここで何をすべきかについてのアイデアはありますか?それは私にとってすべて泥です。前もって感謝します!

4

1 に答える 1

2

おもしろいのは、「コードを切り取った」回答の最も重要な部分を見逃していることです。つまり、中間に Python 関数があり、抽象操作を実行しています ( とラベル付けされていますfunc())。このコードは、関数のトランザクション ラッパーを示しています。上記の例では、代わりにcommit()Session.

ここでは、SQL() と呼ばれるセッション保持オブジェクトのようなものがありますが、これは実際にはプログラムに有用性を追加せず、プログラムを不必要に複雑にしており、おそらく問題の原因でもあります。アプリケーションがさまざまなタイミングでさまざまなデータベースに接続し、SQL()オブジェクトを使用してその状態を表す場合を除き、"エンジン" がくっついた "SQL" というクラスを構築してもあまり意味がありません。scoped_session() と同様に、エンジンをモジュールのどこかに貼り付けるだけです。

エンジンと scoped_session は、ファクトリパターンと呼ばれるパターンを表します。これらは、他の便利なオブジェクトを作成するオブジェクトです。この場合、scoped_session はを作成しSession、データベースと対話するためのを作成するためにEngineによって内部的に使用されます。オブジェクトをand と一緒に兄弟メンバーとして配置することはあまり意味がありません- ファクトリ ( and )、またはそれらが作成するオブジェクト自体( )のいずれかを持ち歩くことになります。しようとしています。SessionConnectionSessionEnginescoped_sessionEnginescoped_sessionSession

それ自体は、工場自体 ( and )ではなく、工場が作成するもの( )Sessionについて話していることを思い出してください。少なくとも少しスレッドセーフではありません。これは通常、関数に対してローカルにのみ作成するものです。グローバルであってはなりません。実際にスレッド間で単一のオブジェクトを使用している場合、おそらくここで問題になります。あなたが得ている実際のエラー、それが何であるかはよくわかりません。ここで使用されている SQLAlchemy の正確なバージョンを知っていれば、より良い手がかりを得ることができますが、エラーのランダム性は、何らかのエラーがあることを示唆しています別のスレッドで同じオブジェクトが存在すると予想されるため、あるスレッドで何かが None になるというスレッドの問題。SessionEnginescoped_sessionSQL()

したがって、このプログラムで確立する必要があるのは、実行の特定のスレッドがいつ開始されるか、スレッドが進行するときにデータベースで何をする必要があるか、そしていつ終了するかです。そのための一貫したパターンを確立できる場合は、Sessionこのスレッドにシングルをリンクします。これは、そのスレッドの存続期間にわたって続き、共有されることはありません。このセッションによって生成されるすべてのオブジェクトも、他のスレッドと共有してはなりません。それらは、セッションの状態の拡張です。「ワーカー スレッド」を使用している場合、それらのワーカー スレッドは、必要に応じて独自のセッション内に独自のデータをロードする必要があります。セッションはライブ データベース トランザクションを表し、通常は単一スレッドに対してローカルなトランザクションが必要です。

これは Web アプリケーションではないためscoped_session、実際にスレッド ローカル パターンを使用する場所がない限り、 の使用を控えた方がよいでしょう。

于 2012-05-07T22:04:05.383 に答える