1

ご存知のように、NHibernate セッションはスレッド セーフではありません。しかし、コード パスがいくつかの実行時間の長いスレッドに分割されており、すべて初期スレッドに読み込まれたオブジェクトを使用しています。

using (var session = factory.OpenSession())
{
    var parent = session.Get<T>(parentId);
    DoSthWithParent(session, parent);
    foreach (var child in parent.children)
    {
        parallelThreadMethodLongRunning.BeginInvoke(session, child);
        //[Thread #1] DoSthWithChild(child #1) -> SaveOrUpdate(child #1) + Flush()
        //[Thread #2] DoSthWithChild(child #2) -> SaveOrUpdate(child #2) + Flush()
        //[Thread #3] DoSthWithChild(child #3) -> SaveOrUpdate(child #3) + Flush()
        // -> etc... changes to be persisted immediately, not all at the end.
        EndInvoke();
    }
    DoFinalChangesOnParentAndChildren(parent);
    session.Flush();
}

}

1 つの方法は各スレッドのセッションですが、その場合は親オブジェクトをそれぞれに再ロードする必要があります。さらに、最後のメソッドは子に対しても変更を行っており、その間に別のセッションが変更した場合、または削除/再ロードする必要があった場合は、StaleObjectException で実行されます。

したがって、すべてのスレッドが同じセッションを使用する必要があります。これを行う最善の方法は何ですか?

  1. メイン スレッドから (EndInvoke() の代わりに) ループでポーリングされる初期スレッド (スレッド セーフな実装) で保存キューを使用します。子スレッドは、メイン スレッドによって保存される NHibernate オブジェクトを挿入できます。

  2. 何らかのコールバック メカニズムを使用して、メイン スレッドでオブジェクトを保存/フラッシュします。WPF、Control.Invoke()、または BackgroundWorker の UI スレッド コールバックに類似したものはありますか?

  3. 保存/フラッシュ アクセスをロック (セッション) ブロックに入れますか? Save()/Flush() を実行しなくても、NHibernate オブジェクトを変更するとセッションが変更される可能性があるため、おそらく危険です。

  4. または、データベースのオーバーヘッドを考慮して、各スレッドの個別のセッションに同じオブジェクトをロードし、メインスレッドでそれらを削除して再ロードしてから、再度変更を行う必要がありますか? [編集: オブジェクトの同時実行/古いオブジェクトのリスクによる悪い「解決策」]

また、アプリケーションには NHibernate の上にビジネス ロジック層があり、同様のオブジェクトがありますが、それ自体の Save() コマンドでそのプロパティ値を NHibernate オブジェクトに送信し、それらを変更して NHibernate Save()/Flush() をすぐに実行することも考慮してください。 .

編集: NHibernate オブジェクトに対する読み取り操作によってセッションが変更される可能性があることが重要です。遅延読み込み、特定の条件下でのチルレン コレクションの変更です。そのため、NHibernate オブジェクトへのすべてのアクセスを同期するビジネス オブジェクト レイヤーを最上位に配置することをお勧めします。データベース操作がスレッドの最小限の時間 (主に時折のステータス設定) しかかからず、ほとんどが計算、監視、Web サービス アクセスなどにかかることを考慮すると、データ レイヤーの同期によるパフォーマンスの低下は無視できます。

4

2 に答える 2

0

まず、私の理解が正しければ、異なるスレッドが同じオブジェクトを更新している可能性があります。その場合、nHibernate であろうとなかろうと、同じオブジェクトに対して複数の更新を同時に実行しているため、予期しない結果が生じる可能性があります。
設計を少し調整して、オブジェクトが (最大でも) 単一のスレッドによってのみ更新されるようにすることができます。

ここで、フローに同じスレッドが同じデータを読み取る (ただし、異なるデータを書き込む) ことが含まれる可能性があると仮定すると、異なるセッション (スレッドごとに 1 つ) を使用し、第 2 レベルのキャッシュを利用することをお勧めします。
第 2 レベルのキャッシュはSessionFactory(ではなくSession) レベルで保持されるため、すべてのセッション インスタンスで共有されます。

于 2013-03-24T20:02:25.593 に答える
0

セッション オブジェクトはスレッド セーフではありません。別のスレッドで使用することはできません。別のスレッドで SaveOrUpdate を実行すると、プログラムがクラッシュするか、データベースが破損する可能性が高くなります。ただし、更新するデータ セットを作成し、メイン スレッドで SaveOrUpdate アクションを実行するのはどうでしょうか (セッションが作成されました)。

NHibernate セッションを作成するときは、次のプラクティスに従う必要があります。 • データベース接続ごとに複数の同時 ISession または ITransaction インスタンスを作成しないでください。

• トランザクションごとにデータベースごとに複数の ISession を作成する場合は、細心の注意を払ってください。ISession 自体は、読み込まれたオブジェクトに対して行われた更新を追跡するため、別の ISession で古いデータが表示される場合があります。

• ISession はスレッドセーフではありません。2 つの同時スレッドで同じ ISession にアクセスしないでください。ISession は通常、単一の作業単位にすぎません。

于 2013-03-21T09:59:16.057 に答える