在AppDomains之间共享数据

ata*_*ata 18 c# appdomain

我有一个可以有多个AppDomain的进程.每个AppDomain都会收集一些统计信息.在指定的时间之后,我想累积这些统计信息并将它们保存到文件中.

一种方法是远程处理,我想避免.

我想到的唯一其他技术是将每个AppDomain的数据保存在一个文件中,并在一段特定时间后,其中一个AppDomain收集所有数据并累积它们.

但如果所有这些都可以在内存中完成,那么这将是理想的,而无需在AppDomains之间序列化信息的成本.有人有主意吗?

Alo*_*aus 28

可以在AppDomains之间共享数据,而无需编组.但这是一种相当黑客的方式.您可以创建一个源数据对象,该对象在所有AppDomain之间通过引用共享.这样,您可以将所有数据放入一个共享对象,而无需编组.听起来太容易了吗?

首先要知道如何在没有编组的情况下在AppDomains之间共享数据.为此,您可以通过Marshal.UnsafeAddrOfPinnedArrayElement获取数据源对象的对象地址.然后将此IntPtr传递给对此感兴趣的所有AppDomain.在目标AppDomain中,您需要将此IntPtr转换回对象引用,该对象引用可以完成JIT :: CastAny,如果从方法返回一个对象并将其指针推送到堆栈上,则完成该操作.

Viola您已经将对象作为AppDomains之间的普通指针共享,并且您获得了InvalidCastExceptions.问题是您必须为所有AppDomains LoaderOptimization.MultiDomain设置,以确保定义共享数据类型的程序集作为AppDomain中性类型加载,该类型在所有AppDomain之间具有相同的方法表指针.

您可以找到一个示例应用程序,它作为WMemoryProfiler的一部分完成此操作.请参阅此链接以获取更详细的说明并下载示例代码的链接.

基本代码是

[LoaderOptimization(LoaderOptimization.MultiDomain)]
static public void Main(string[] args)
{

    // To load our assembly appdomain neutral we need to use MultiDomain on our hosting and child domain
    // If not we would get different Method tables for the same types which would result in InvalidCastExceptions
    // for the same type.
    var other = AppDomain.CreateDomain("Test"+i.ToString(), AppDomain.CurrentDomain.Evidence, new AppDomainSetup
        {
            LoaderOptimization = LoaderOptimization.MultiDomain,
        });

    // Create gate object in other appdomain
    DomainGate gate = (DomainGate)other.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(DomainGate).FullName);

    // now lets create some data
    CrossDomainData data = new CrossDomainData();
    data.Input = Enumerable.Range(0, 10).ToList();

    // process it in other AppDomain
    DomainGate.Send(gate, data);

    // Display result calculated in other AppDomain
    Console.WriteLine("Calculation in other AppDomain got: {0}", data.Aggregate);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 真的很好的答案。我首先怀疑在这样的两个 AppDomain 之间共享相同的对象是否会对任何未固定对象(只有一个共享对象被固定)的 GC 造成严重破坏。但是正如您在文章中很好地概述的那样,所有 AppDomain 中只有一个 GC,所以它是可行的。很酷的东西! (2认同)

Ada*_*lph 15

避免序列化的唯一方法是使用派生自MarshalByRefObject的对象来表示您的数据,但在这种情况下,您仍然需要跨AppDomain边界进行编组的成本.这也可能涉及重构/重写大部分代码.

假设通过引用进行编组不是一个选项,则必须在某个时候进行序列化.这根本无法避免.一种方法是使用Neil Barnwell建议,使用数据库,另一种方法是使用您自己建议的本地文件.

根据您的交付时间表和/或.NET 4.0采用,另一种可能或不可行的方法是使用内存映射文件,请参阅.Net Framework 4.0:使用内存映射文件.

  • 如果你遵循这条道路,不要忘记覆盖InitializeLifetimeService方法; 这让我疯狂了几天("对象'......'已断开连接或服务器上不存在.") (4认同)