web-dev-qa-db-ja.com

別のAppDomainでデリゲートを渡して実行する

デリゲートを使用して、別のAppDomainでコードの一部を実行したいと思います。これどうやってするの?

UPD1:問題に関する詳細データを処理するプログラム(1回の反復:DBからデータを取得し、評価して、実行時にアセンブリを作成します。動的アセンブリを実行し、結果をDBに書き込みます)。

現在の解決策:各反復は別々のスレッドで実行されます。より良い解決策:各反復は別々のAppDomainで実行されます(動的アセンブリをアンロードするため)。

UPD2:すべて、回答ありがとうございます。

私はこのスレッドで私のために1つを見つけました: Process.StartをAppDomainsで置き換えます

31
lak-b

別のAppDomainによって処理されるデリゲートを呼び出すことはできますが、私は個人的に、外部アプリドメインにオブジェクトを作成してプロキシを返す「CreateInstanceAndUnwrap」メソッドを常に使用しています。

これが機能するためには、オブジェクトがMarshalByRefObjectから継承する必要があります。

次に例を示します。

    public interface IRuntime
    {
        bool Run(RuntimesetupInfo setupInfo);
    }

    // The runtime class derives from MarshalByRefObject, so that a proxy can be returned
    // across an AppDomain boundary.
    public class Runtime : MarshalByRefObject, IRuntime
    {
        public bool Run(RuntimeSetupInfo setupInfo)
        {
            // your code here
        }
    }

    // Sample code follows here to create the appdomain, set startup params
    // for the appdomain, create an object in it, and execute a method
    try
    {
        // Construct and initialize settings for a second AppDomain.
        AppDomainSetup domainSetup = new AppDomainSetup()
        {
            ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
            ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
            ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
            LoaderOptimization = LoaderOptimization.MultiDomainHost
        };

        // Create the child AppDomain used for the service tool at runtime.
        childDomain = AppDomain.CreateDomain(
            "Your Child AppDomain", null, domainSetup);

        // Create an instance of the runtime in the second AppDomain. 
        // A proxy to the object is returned.
        IRuntime runtime = (IRuntime)childDomain.CreateInstanceAndUnwrap(
            typeof(Runtime).Assembly.FullName, typeof(Runtime).FullName);

        // start the runtime.  call will marshal into the child runtime appdomain
        return runtime.Run(setupInfo);
    }
    finally
    {
        // runtime has exited, finish off by unloading the runtime appdomain
        if(childDomain != null) AppDomain.Unload(childDomain);
    }

上記のサンプルでは、​​いくつかのセットアップ情報を渡す 'Run'メソッドを実行するようにコード化されており、Runメソッドの完了は、子AppDomain内のすべてのコードの実行が完了したことを示すと判断されるため、finallyブロックがあります。 AppDomainがアンロードされます。

多くの場合、どのタイプをどのアセンブリに配置するかについて注意する必要があります。インターフェイスを使用して、呼び出し元(appdomainを設定し、それを呼び出すコード)と実装者(ランタイムクラス)はに依存しています。このIIRCを使用すると、親AppDomainはインターフェイスを含むアセンブリのみをロードでき、子appdomainはランタイムを含むアセンブリとその依存関係(IRuntimeアセンブリ)の両方をロードできます。 IRuntimeインターフェイスで使用されるユーザー定義型(RuntimeSetupInfoクラスなど)も、通常、IRuntimeと同じアセンブリに配置する必要があります。また、これらのユーザー定義型を定義する方法に注意してください-それらがデータ転送オブジェクトである場合(RuntimeSetupInfoがおそらくそうであるように)、おそらく[serializable]属性でそれらをマークする必要があります-オブジェクトのコピーが渡されるように(からシリアル化されます子の親appdomain)。これはかなり遅いので、あるアプリドメインから別のアプリドメインにコールがマーシャリングされるのを避けたいと思います。 DTOを値で渡す(シリアル化)とは、DTOの値にアクセスしても、アパートメント間の呼び出しが発生しないことを意味します(子appdomainには元の独自のコピーがあるため)。もちろん、これは、値の変更が親アプリドメインの元のDTOに反映されないことも意味します。

例でコーディングされているように、親アプリドメインは実際にはIRuntimeアセンブリとRuntimeアセンブリの両方をロードすることになりますが、これはCreateInstanceAndUnwrapの呼び出しでtypeof(Runtime)を使用してアセンブリ名と完全修飾型名を取得しているためです。代わりに、これらの文字列をファイルからハードコーディングまたは取得することができます。これにより、依存関係が切り離されます。

AppDomainには、「DoCallBack」という名前のメソッドもあります。これは、外部のAppDomainでデリゲートを呼び出すことができるように見えます。ただし、使用するデリゲート型は「CrossAppDomainDelegate」型です。その定義は次のとおりです。

public delegate void CrossAppDomainDelegate()

したがって、データを渡すことはできません。そして、私はそれを使ったことがないので、特定の落とし穴があるかどうかはわかりません。

また、LoaderOptimizationプロパティを調べることをお勧めします。これを設定すると、パフォーマンスに大きな影響を与える可能性があります。これは、このプロパティの設定によっては、(IIRC)アセンブリがGACにある場合でも(IIRC)、新しいappdomainがすべてのアセンブリの個別のコピー(およびJITなど)をロードするように強制するためです(つまり、これにはCLRアセンブリが含まれます)。子appdomainから多数のアセンブリを使用する場合、これはひどいパフォーマンスをもたらす可能性があります。たとえば、子アプリドメインのWPFを使用したため、より適切な読み込みポリシーを設定するまで、アプリの起動が大幅に遅れました。

49
Phil

別のAppDomainでデリゲートを実行するには、 System.AppDomain.DoCallBack() を使用できます。リンクされたMSDNページには優れた例があります。タイプ CrossAppDomainDelegate のデリゲートのみを使用できることに注意してください。

8
cdiggins

AppDomainsを通過できるのはこれらだけなので、 。NET Remoting 、特に Remote Objects を確認する必要があります。

その長所と短所は、オブジェクトが値によってまたは参照によって(プロキシ経由で)渡されることです。

値では、オブジェクトがシリアル化可能である必要があります。デリゲートはシリアル化可能なafaikではありません。つまり、これは従うのに適したルートではないということです。

参照により、 MarshalByRefObject から継承する必要があります。このようにして、リモートインフラストラクチャはプロキシを作成できます。ただし、デリゲートは、クライアントアプリドメインではなく、デリゲートを作成するマシンで実行されることも意味します。

全体として、それはトリッキーになるでしょう。デリゲートを本格的なシリアル化可能なオブジェクトにして、リモート処理で簡単に移動できるようにすることを検討することをお勧めします(他のテクノロジでもうまく機能します)。

6
Frank Krueger

これはあなたの質問に直接答えるものではありませんが、分離を維持するために、他のAppDomainにWCFサービスまたはWebサービスを作成する方がよいでしょう。私はあなたの特定の状況を知りませんが、孤立した建築設計はほとんど常に正しい道です。

3
Andrew Hare