在单独的 AppDomain 中传递和执行委托 [英] Pass and execute delegate in separate AppDomain

查看:21
本文介绍了在单独的 AppDomain 中传递和执行委托的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用委托在单独的 AppDomain 中执行一些代码.我该怎么做?

I want to exceute some piece of code in separate AppDomain with delegate. How can I do this?

UPD1:关于我的问题的更多细节我的程序处理一些数据(一次迭代是:从 DB 获取一些数据,评估它并在运行时创建程序集,执行动态程序集并将结果写入 DB).

UPD1: some more details about my problem My program processing some data (one iteration is: get some data from DB, evaluate it and create assemblies at runtime, execute dynamic assemblies and write results to DB).

当前的解决方案:每次迭代都在单独的线程中运行.更好的解决方案:每次迭代都在单独的 AppDomain 中运行(以卸载动态组件).

Current solution: each iteration running in separate thread. Better solution: each iteration running in separate AppDomain (to unload dynamic asseblies).

UPD2:谢谢大家的回答.

我在这个线程中找到了一个:替换 Process.Start with AppDomains

I have found one for me in this thread: Replacing Process.Start with AppDomains

推荐答案

尽管您可以调用由单独的 AppDomain 处理的委托,但我个人一直使用CreateInstanceAndUnwrap"方法,该方法在外部应用域并返回一个代理给它.

Although you can make a call into a delegate which will be handled by a separate AppDomain, I personally have always used the 'CreateInstanceAndUnwrap' method which creates an object in the foreign app domain and returns a proxy to it.

为此,您的对象必须从 MarshalByRefObject 继承.

For this to work your object has to inherit from 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 的块.

In the above sample, it is coded to execute a 'Run' method passing in some setup information, and completion of the Run method is determined to indicate that all code in the child AppDomain has completed running, so we have a finally block that ensures the AppDomain is unloaded.

您通常可能需要小心将哪些类型放置在哪些程序集中 - 您可能希望使用一个接口并将其放置在一个单独的程序集中,调用方(我们设置应用程序域的代码,并调用它的代码)) 和实现者(运行时类)依赖.这个 IIRC 允许父 AppDomain 只加载包含接口的程序集,而子应用程序域将加载包含运行时的程序集及其依赖项(IRuntime 程序集).IRuntime 接口使用的任何用户定义类型(例如我们的 RuntimeSetupInfo 类)通常也应该与 IRuntime 放在同一个程序集中.此外,请注意您如何定义这些用户定义的类型 - 如果它们是数据传输对象(如 RuntimeSetupInfo 可能是),您可能应该使用 [serializable] 属性标记它们 - 以便传递对象的副本(从父应用程序域到子项).您希望避免将调用从一个应用程序域编组到另一个应用程序域,因为这非常慢.按值传递 DTO(序列化)意味着访问 DTO 上的值不会引起跨单元调用(因为子应用程序域拥有自己的原始副本).当然,这也意味着值的变化不会反映在父应用域的原始 DTO 中.

You often may want to be careful in which types you place in which assemblies - you may want to use an interface and place it in a separate assembly that both the caller (our code that sets up the appdomain, and calls into it) and the implementer (the Runtime class) are dependent on. This IIRC allows the parent AppDomain to only load the assembly that contains the interface, while the child appdomain will load both the assembly that contains Runtime and it's dependency (the IRuntime assembly). Any user defined types that are used by the IRuntime interface (e.g. our RuntimeSetupInfo class) should usually also be placed in the same assembly as IRuntime. Also, be careful of how you define these user defined types - if they are data transfer objects (as RuntimeSetupInfo probably is), you should probably mark them with the [serializable] attribute - so that a copy of the object is passed (serialized from the parent appdomain to the child). You want to avoid calls being marshalled from one appdomain to another since this is pretty slow. Passing DTOs by value (serialization) means accessing values on the DTO doesn't incur a cross-apartment call (since the child appdomain has it's own copy of the original). Of course, this also means that value changes are not reflected in the parent appdomain's original DTO.

正如示例中编码的那样,父应用程序域实际上最终会同时加载 IRuntime 和运行时程序集,但这是因为在调用 CreateInstanceAndUnwrap 时,我使用 typeof(Runtime) 来获取程序集名称和完全限定的类型名称.您可以改为从文件中硬编码或检索这些字符串 - 这将解耦依赖关系.

As is coded in the example, the parent appdomain will actually end up loading both the IRuntime and Runtime assemblys but that is because in the call to CreateInstanceAndUnwrap I am using typeof(Runtime) to get the assembly name and fully qualified type name. You could instead hardcode or retrieve these strings from a file - which would decouple the dependency.

AppDomain 上还有一个名为DoCallBack"的方法,它看起来允许调用外部 AppDomain 中的委托.但是,它采用的委托类型是CrossAppDomainDelegate"类型.其中的定义是:

There also is a method on AppDomain named 'DoCallBack' which looks like it allows calling a delegate in a foreign AppDomain. However, the delegate type that it takes is of type 'CrossAppDomainDelegate'. The definition of which is:

public delegate void CrossAppDomainDelegate()

因此,它不允许您向其中传递任何数据.而且,由于我从未使用过它,所以我无法告诉您是否有任何特别的问题.

So, it won't allow you to pass any data into it. And, since I've never used it, I can't tell you if there are any particular gotchas.

另外,我建议您查看 LoaderOptimization 属性.您将其设置为什么,可能会对性能产生重大影响,因为此属性的某些设置会强制新的应用程序域加载所有程序集的单独副本(并对其进行 JIT 等),即使 (IIRC) 程序集在 GAC 中(即这包括 CLR 程序集).如果您使用子应用程序域中的大量程序集,这会给您带来可怕的性能.例如,我使用了子应用程序域中的 WPF,这导致我的应用程序启动延迟很大,直到我设置了更合适的加载策略.

Also, I'd recommend looking into the LoaderOptimization property. What you set this to, can have a significant affect on performance, since some settings of this property force the new appdomain to load separate copies of all assemblies (and JIT them etc.) even if (IIRC) the assembly is in the GAC (i.e. this includes CLR assemblies). This can give you horrible performance if you use a large # of assemblies from your child appdomain. For e.g., I've used WPF from child appdomains which caused huge startup delays for my app until I setup a more appropriate load policy.

这篇关于在单独的 AppDomain 中传递和执行委托的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆