【问题标题】:Pass and execute delegate in separate AppDomain在单独的 AppDomain 中传递和执行委托
【发布时间】:2011-01-01 19:05:32
【问题描述】:

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

UPD1:关于我的问题的更多详细信息 我的程序处理一些数据(一个迭代是:从数据库中获取一些数据,对其进行评估并在运行时创建程序集,执行动态程序集并将结果写入数据库)。

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

UPD2:谢谢大家的回答。

我在这个帖子中为我找到了一个: Replacing Process.Start with AppDomains

【问题讨论】:

    标签: c# delegates appdomain


    【解决方案1】:

    虽然您可以调用将由单独的 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 的块。

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

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

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

    public delegate void CrossAppDomainDelegate()
    

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

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

    【讨论】:

    • CreateInstanceAndUnwrap 方法抛出一个错误,指出它找不到 (typeof(Runtime).Assembly.FullName) 指定的程序集。这是在我的网络服务器上运行的,全名的格式是“myhostdll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”。
    • @Triynko,在创建新的 AppDomain 设置时,尝试指定与主域相同的 bin 文件夹,或者至少使用新域可以获取程序集的位置。
    • 在我的情况下,Outlook 进程创建了两个应用程序域,一个是我的,另一个来自我正在集成的供应商。由于我无法将其中一个域显式创建为另一个域的子域,您的代码仍然可行吗?
    【解决方案2】:

    要在另一个 AppDomain 上执行委托,您可以使用 System.AppDomain.DoCallBack()。链接的 MSDN 页面有一个很好的例子。请注意,您只能使用 CrossAppDomainDelegate 类型的委托。

    【讨论】:

      【解决方案3】:

      您需要阅读.NET Remoting,特别是Remote Objects,因为这些都是您可以通过AppDomains 传递的。

      总而言之,您的对象要么通过按值通过引用(通过代理)传递。

      按值要求您的对象是可序列化的。代表不可序列化 afaik。这意味着这不是一条好路。

      通过引用要求您从MarshalByRefObject 继承。这样,远程基础设施可以创建代理。但是,这也意味着您的委托将在创建它的机器上执行,而不是在客户端应用程序域上。

      总而言之,这会很棘手。您可能需要考虑使您的委托完全成熟的可序列化对象,以便可以通过远程处理轻松移动它们(并且可以与其他技术很好地配合使用)。

      【讨论】:

      • 您确定可以使用任何委托吗?据我所知,正如我的帖子中提到的,您只能使用“CrossAppDomainDelegate”类型的委托
      • 可以使用任何返回 void 且没有参数的委托,但可以通过将引用传递给您的方法来创建 CrossAppDomainDelegate。示例:otherDomain.DoCallBack(new CrossAppDomainDelegate(MyCallBack)).
      【解决方案4】:

      这并不能直接回答您的问题,但也许最好在另一个 AppDomain 中创建 WCF 服务或 Web 服务以保持隔离。我不知道你的具体情况,但孤立的建筑设计几乎总是正确的方法。

      【讨论】:

      • 实际上,我正在创建这样的服务。我想在单独的域中执行每个请求。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-07
      • 1970-01-01
      相关资源
      最近更新 更多