【问题标题】:How to speed up Cross-AppDomain calls?如何加快跨 AppDomain 调用?
【发布时间】:2015-02-11 14:46:37
【问题描述】:

我们开发了一些系统,可以与数据库队列一起使用,并且哪些组件驻留在单独的 Windows 服务中。我们的想法是在单个可执行文件中执行所有组件,但在单独的 AppDomain 中。主要目的是防止单个组件崩溃时整个系统崩溃。

在进行了几次测试后,我们遇到了跨应用域调用太慢的问题。我尝试使用强名称对程序集进行签名并将它们放入 GAC 并应用 LoaderOptimization.MultiDomain(Host),但性能仍然无法接受:14 000 000 次调用/秒,简单的单个 AppDomain 调用和 90 000 次调用/秒跨域通话模式。在应用程序中执行了两个调用:第一个从 Default AppDomain 到 Non-Default AppDomain,第二个调用 - 方向相反。 90 000 次调用/秒是非常好的结果,但考虑到测试应用程序的简单性以及将我们系统的复杂性投射到多 AppDomain 方法后,我们发现它太慢了。

主要问题是:有没有办法显着加快跨域调用?也许可以消除某种检查?

代码:

class Program
{
    public class TestMessage : MarshalByRefObject { }

    public class TestClass : MarshalByRefObject
    {
        private TestClass() { }


        public void TestMethod(TestMessage message) { }


        public static TestClass Create(bool defaultDomain)
        {
            if (defaultDomain)
            {
                return TestClass.createDefaultDomain();
            }

            return TestClass.createInNewDomain();
        }


        private static TestClass createDefaultDomain()
        {
            return new TestClass();
        }

        private static TestClass createInNewDomain()
        {
            AppDomain appDomain = AppDomain.CreateDomain("NewDomain");

            return appDomain.CreateInstance(
                typeof(TestClass).Assembly.FullName,
                typeof(TestClass).FullName,
                true, BindingFlags.NonPublic | BindingFlags.Instance,
                null, null, null, null)
                .Unwrap() as TestClass;
        }
    }

    [STAThread]
    [LoaderOptimization(LoaderOptimization.MultiDomain)]
    static void Main(string[] args)
    {
        TestClass testClass = TestClass.Create(true);
        TestMessage message = new TestMessage();

        int calls = 0;

        Stopwatch sw = Stopwatch.StartNew();

        while (sw.ElapsedMilliseconds < 10000)
        {
            testClass.TestMethod(message);

            calls++;
        }

        sw.Start();

        Console.WriteLine("{0:N3}", calls / sw.Elapsed.TotalSeconds);
    }
}

【问题讨论】:

  • AppDomain 概念的一个要点是提供隔离。删除安全检查将否定这一点,我认为这是不可能的。至少部分缓慢来自于跨 AppDomain 边界封送数据。因此,一种方法是努力将其最小化。例如,仅传递值类型或字符串。如果需要,请进行自己的参数序列化。如果剩余的 AppDomain 开销仍然无法接受,或许可以使用不同的机制在 AppDomain 之间进行通信,例如命名管道、套接字、共享内存等。
  • @PeterDuniho,当跨 AppDomains 传递纯可序列化对象时,性能相当高,但问题是我们需要通过 AppDomain-s 传递 MarshalByRefObj 派生对象,这确实会降低性能。跨度>
  • 无论它们如何传递,即使您的 MarshalByRefObject 类型也将被序列化/反序列化。唯一的问题是您是否将其留给 .NET,或者您自己做。我的观点是,如果您可以接管该过程,使得实际的跨域调用只是一个字符串(即作为字符串的序列化对象),那么您可以更好地控制序列化并对其进行优化(例如,如果同一个对象被多次传递,序列化一次,每次调用都重用序列化的字符串)。
  • @PeterDuniho,我们需要传递 .NET 对象,它们是 MarshalByRegObj-s 并且没有标记为可序列化,恐怕无法序列化它们。
  • 我明白了。如果你必须使用MarshalByRefObject,那么你似乎被困住了。但是如果这些对象是MarshalByRefObject 的唯一原因是您可以跨域使用它们,那么您应该能够通过更轻量级的实现来完成类似的结果(即不使用MarshalByRefObject 而是将跨域处理域通信自己)

标签: c# .net performance cross-domain appdomain


【解决方案1】:

seems 拥有多个 AppDomain,你倒霉了:

直接调用只是几个操作码,但远程处理涉及多个类、真实/透明代理、安全检查、序列化

如果您唯一担心的是组件崩溃不会传播(而不是阻止与动态加载到单个 AppDomain 相关联的memory leak,或阻止直接访问),您可以通过 try/捕获这些组件中的所有代码调用。如果您已经在考虑/使用多个 AppDomain,那么无论如何都不会有那么多不同的交互点,因此请尝试/捕捉它,并可能在您的(自行创建的)线程的最外层尝试/捕捉 - 这是一个总的来说还是个好主意 - 可能就足够了。

但是,如果您需要防止随机的第三方代码访问其他代码/数据,那么单个域是不可能的。

【讨论】:

    猜你喜欢
    • 2013-02-16
    • 1970-01-01
    • 2018-01-20
    • 1970-01-01
    • 2013-02-15
    • 2011-02-14
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多