6

IoCコンテナとしてSimpleInjectorを使用しています。SimpleInjectorは、この単純な手法を使用して、スレッドごととWeb要求ごとの混合ライフスタイルを処理します

container.RegisterPerWebRequest<IWebRepository, Repository>();
container.RegisterLifetimeScope<IThreadRepository, Repository>();
container.Register<IRepository>(container.GetInstance<Repository>());

// Register as hybrid PerWebRequest / PerLifetimeScope.
container.Register<Repository>(() =>
{
    Repository repository;
    if (HttpContext.Current != null)
        repository = (Repository)container.GetInstance<IWebRepository>();
    else
        repository = (Repository)container.GetInstance<IThreadRepository>();

    return repository;
});

残念ながら(そして明らかに!)、UnitOfWorkクラスの他の場所で、Parallel.ForEachを使用して、最初のスレッドのみがHttpContextで値を検出するため、Repositoryクラスの複数のインスタンスを並列に呼び出そうとすると問題が発生します。現在

using (TransactionScope scope = new TransactionScope())
{
    Parallel.ForEach(new List<IRepository>() { _repository1, _repository2 ... }, 
        (repository) =>
        {
            repository.Commit();
        });
    scope.Complete();
}

質問を書き終えたので、おそらく不可能か何か愚かなことを求めていることがわかります...しかし、一体何が...これはできるのでしょうか?単一のリクエスト/スレッド登録を複数の内部スレッドで利用できるようにすることはできますか?

4

1 に答える 1

10

依存性注入を使用すると、オブジェクトの存続期間に関する知識を一元化しようとします。この一元化された場所は、コンポジションルートと呼ばれます。あるスレッドから別のスレッドに依存関係を渡し始めるとき、コードのそれらの部分は、それらの依存関係を安全に渡すことができるかどうかを知る必要があります。たとえば、これらの依存関係はスレッドセーフですか?それらの依存関係は、その新しいコンテキスト(たとえば、HTTPまたはWCF要求の外部)で実行できますか?これは多くの状況で分析するのは簡単かもしれませんが、他の実装でこれらの依存関係を変更することはできません。これは、コード内にこれが発生する場所があり、どの依存関係が渡されるかを知る必要があるためです。この知識を再び分散化することで、DI構成の正確さについて推論することが難しくなり、コードが直接失敗する(せいぜい)またはデバッグが困難な競合状態を引き起こすような方法でコンテナーを誤って構成することが容易になります(最悪の場合)。

したがって、新しく開始された各スレッドに、コンテナに新しいオブジェクトグラフを要求して、新しいオブジェクトグラフを作成させるのが最も安全です。依存関係(つまり、コンテナーによって管理および注入されるインスタンス)を1つのスレッドから別のスレッドに渡さないでください。

あなたの場合、リポジトリは他のスレッドに送信できないように見えるため、HTTPコンテキストと関係があるように見えるため、問題が発生するようにさえ見えます。その場合、それはかなり「簡単」です:これをしないでください;-)

これは、パフォーマンスを高速化するためにマルチスレッドを実行できないことを意味するものではありません。ただし、この操作によって依存関係が1つのスレッドからスレッドに移動しないこと、または移動するときに移動しないことを確認する必要があります。このコード(この場合はParallel.ForEach)がアプリケーションのコンポジションルート内にあることを確認してください。これは、この操作が安全に実行できるかどうかを知ることができる/すべきである唯一の場所だからです。

ただし、特定のケースでは、複数のスレッドでコミットしているのは非常に怖いです。その作業単位のコミットはアトミックであるべきではありませんか?異なるスレッドでリポジトリコミットを実行するときに、このコミットがまだアトミックであることを確認しますか?コミットの1つが失敗したが、他のコミットは成功した場合はどうなりますか?すでに成功した操作をどのようにロールバックしますか?

私はあなたがこれらの問題を解決できると思います(そしてあなたはすでに解決しているかもしれません)が、そのような解決策はシステムに多くの複雑さを追加します。パフォーマンスの向上が追加された複雑さを補うかどうかを自問する必要があります。

マルチスレッドアプリケーションでの依存性注入の操作について詳しくは、こちらをご覧ください。

于 2012-12-21T09:04:08.157 に答える