【发布时间】: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