C# 在单独的 AppDomain 中传递和执行委托

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2008691/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 22:37:15  来源:igfitidea点击:

Pass and execute delegate in separate AppDomain

c#delegatesappdomain

提问by lak-b

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

我想用委托在单独的 AppDomain 中执行一些代码。我怎样才能做到这一点?

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

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

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

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

UPD2: All, thanks for answers.

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

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

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

采纳答案by Phil

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.

尽管您可以调用由单独的 AppDomain 处理的委托,但我个人一直使用“CreateInstanceAndUnwrap”方法,该方法在外部应用程序域中创建一个对象并将代理返回给它。

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

为此,您的对象必须从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.

在上面的示例中,它被编码为执行一个'Run'方法并传入一些设置信息,并且确定Run方法的完成表明子AppDomain中的所有代码都已完成运行,因此我们有一个finally块来确保AppDomain 已卸载。

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.

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

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

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:

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

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

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

回答by Andrew Hare

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.

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

回答by Frank Krueger

You need to read up on .NET Remotingand specifically on Remote Objectsas these are all you can pass through AppDomains.

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

The long and short of it is that your object is either passed by valueor 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.

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

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.

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

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

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

回答by cdiggins

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.

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