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

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

问题描述

我想要在独立的AppDomain中使用代理来优化一些代码。如何做到这一点?



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



当前解决方案:每个迭代在单独的线程中运行。
更好的解决方案:每个迭代运行在单独的AppDomain(卸载动态asseblies)。



UPD2 :所有,谢谢答案。



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

解决方案

虽然你可以打电话代理将由单独的AppDomain处理,我个人一直使用'CreateInstanceAndUnwrap'方法,在外部应用程序域中创建一个对象并返回一个代理。



为了这个工作,你的对象必须继承自MarshalByRefObject 。



这是一个例子:

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

//运行时类派生自MarshalByRefObject,以便代理可以通过AppDomain边界返回
//。
public class Runtime:MarshalByRefObject,IRuntime
{
public bool Run(RuntimeSetupInfo setupInfo)
{
//你的代码
}
}

//示例代码遵循这里创建appdomain,为appdomain设置启动参数
//,在其中创建一个对象,并执行一个方法
try
{
//为第二个AppDomain构造和初始化设置。
AppDomainSetup domainSetup = new AppDomainSetup()
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
LoaderOptimization = LoaderOptimization.MultiDomainHost
};

//在运行时创建用于服务工具的​​子AppDomain。
childDomain = AppDomain.CreateDomain(
Your Child AppDomain,null,domainSetup);

//在第二个AppDomain中创建一个运行时实例。
//返回对象的代理。
IRuntime runtime =(IRuntime)childDomain.CreateInstanceAndUnwrap(
typeof(Runtime).Assembly.FullName,typeof(Runtime).FullName);

//启动运行时。调用将组织到子运行时appdomain
return runtime.Run(setupInfo);
}
finally
{
//运行时已经退出,通过卸载运行时appartain
完成(如果(childDomain!= null))AppDomain.Unload(childDomain);
}

在上面的示例中,它被编码为执行运行方法传递在某些设置信息中,并且确定运行方法的完成以指示子AppDomain中的所有代码已经完成运行,所以我们有一个finally块来确保AppDomain被卸载。



您经常希望在哪些类型中放置哪些程序集 - 您可能希望使用接口并将其放在单独的程序集中,这两个程序集既可以是调用者(我们的代码设置appdomain,也可以调用它)和实现者(Runtime类)依赖于。此IIRC允许父AppDomain仅加载包含该接口的程序集,而子应用程序将加载包含Runtime的程序集及其依赖关系(IRuntime程序集)。 IRuntime接口(例如我们的RuntimeSetupInfo类)使用的任何用户定义的类型通常也应该放在与IRuntime相同的程序集中。另外,请注意如何定义这些用户定义的类型 - 如果它们是数据传输对象(如RuntimeSetupInfo可能是),则应该使用[serializable]属性标记它们,以便传递对象的副本(从父母的appdomain给孩子)。你希望避免从一个appdomain到另一个appdomain的通话,因为这是相当缓慢的。通过价值(序列化)传递DTO意味着访问DTO上的值不会产生跨公寓通话(因为子应用程序拥有自己的原始副本)。当然,这也意味着价值的改变并没有反映在父应用程式的原始DTO中。



如示例编码一样,父应用域实际上最终将加载两者IRuntime和Runtime程序集,但这是因为在调用CreateInstanceAndUnwrap时,我使用typeof(Runtime)获取程序集名称和完全限定类型名称。您可以改为从一个文件中硬编码或检索这些字符串 - 这将使依赖关系脱钩。



AppDomain上还有一个名为DoCallBack的方法,它似乎允许调用在外国AppDomain中的代表。但是,它所使用的委托类型是CrossAppDomainDelegate类型。其定义是:

  public delegate void CrossAppDomainDelegate()

所以,它不会允许你传递任何数据。而且,由于我从来没有使用过,我不能告诉你是否有特别的问题。



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


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

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).

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

UPD2: All, thanks for answers.

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

解决方案

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.

For this to work your object has to inherit from MarshalByRefObject.

Here is an example:

    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);
    }

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.

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.

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.

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.

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天全站免登陆