在单独的 AppDomain 中传递并执行委托

发布于 2024-08-17 01:59:45 字数 422 浏览 5 评论 0原文

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

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

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

UPD2:全部,感谢您的回答。

我在这个线程中找到了一个适合我的: 用 AppDomains 替换 Process.Start

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

苦妄 2024-08-24 01:59:45

尽管您可以调用将由单独的 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 中的所有代码都已完成运行,因此我们有确保卸载 AppDomain 的 finally 块。

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

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

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

public delegate void CrossAppDomainDelegate()

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

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

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.

酒解孤独 2024-08-24 01:59:45

In order to execute a delegate on another AppDomain you can use System.AppDomain.DoCallBack(). The linked MSDN page has an excellent example. Note that You can only use delegates of type CrossAppDomainDelegate.

决绝 2024-08-24 01:59:45

您需要阅读.NET Remoting特别是远程对象,因为这些都是您可以通过AppDomains。

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

按值要求您的对象是可序列化的。据我所知,委托是不可序列化的。这意味着这不是一条好路线。

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

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

You need to read up on .NET Remoting and specifically on Remote Objects as these are all you can pass through AppDomains.

The long and short of it is that your object is either passed by value or by reference (via a proxy).

By value requires that your object be Serializable. Delegates are not serializable afaik. That means that this is not a good route to follow.

By reference requires that you inherit from MarshalByRefObject. This way, the remoting infrastructure can create the proxy. However, it also means that your delegate will be executed on the machine that creates it - not on the client app domain.

All in all, it's gonna be tricky. You might want to consider making your delegates full fledged serializable objects so that they can be easily moved around with remoting (and will work well with other technologies).

无法言说的痛 2024-08-24 01:59:45

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

This doesn't answer your question directly but perhaps it would be better to create a WCF service or web service in the other AppDomain to preserve isolation. I don't know your particular situation but isolated architectural design is almost always the right way to go.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文