【问题标题】:Sharing data between AppDomains在 AppDomain 之间共享数据
【发布时间】:2011-01-13 11:58:29
【问题描述】:

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

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

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

但是如果这一切都可以在内存中完成,而不需要序列化信息以在 AppDomain 之间传递的成本,那将是理想的。有人有什么想法吗?

【问题讨论】:

    标签: c# appdomain


    【解决方案1】:

    可以在 AppDomain 之间共享数据而无需编组成本。但这是一种相当hacky的方式。您可以创建一个在所有 AppDomain 之间通过引用共享的源数据对象。通过这种方式,您可以将所有数据放入一个共享对象中,而无需编组成本。听起来太容易令人难以置信?

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

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

    您可以找到作为 WMemoryProfiler 的一部分执行此操作的示例应用程序。有关示例代码的更多detailed explanation and download link,请参阅此链接。

    基本代码是

    [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);
        }
    }
    

    【讨论】:

    • 非常好的答案。我首先怀疑是否像这样在两个 AppDomain 之间共享相同的对象是否会对任何未固定对象的 GC 原因造成严重破坏(只有一个共享对象被固定)。但是正如您在文章中很好地概述的那样,所有 AppDomain 中只有一个 GC,所以它可以解决。很酷的东西!
    • 放弃到非托管代码以尝试绕过框架中的有意安全检查是构建不可维护集群的一种非常好的方法。出于安全性和代码安全性考虑,appdomains 是分开的,这是有充分理由的,并且 MS 清楚地表明您要么 MarshallByRef 要么在需要在两者之间传递时进行序列化。这是一条糟糕的路,充满了糟糕的结局。这对于性能极其敏感的代码、极其包含的代码可能是可以接受的,但对其他人来说却是一个糟糕的一般建议。线程和异步将对此造成严重破坏。
    • @Tim:我不希望任何人使用它,除非用于教育目的。但是由于所有人都在追求不支持 AppDomain 的 .NET Core,因此这个答案无论如何都不再重要了,因为 .NET Core 在性能方面还有更多的优势。
    • 非常正确。核心会随着打磨而变得更好
    【解决方案2】:

    我倾向于说只使用远程处理。将数据写入文件也需要序列化。无论您使用什么技术,序列化似乎几乎是不可避免的。您必须使用某个通道将数据从一个应用程序域传输到另一个应用程序域,并且您必须对数据进行序列化才能通过通道获取数据。

    避免序列化的唯一方法似乎是使用共享内存,以便两个应用程序域无需通过通道即可访问数据。即使将数据从一个应用程序域的内存深度克隆到另一个应用程序域的内存,其核心也不过是二进制序列化(结果不一定存储在连续的内存位置)。

    【讨论】:

    • Remoting 也涉及到反射。那就是序列化+反射。另一方面,我的数据只是一些 long 和 double 值,我可以在文件中写入而无需太多开销。
    • 你看错地方了。使用文件的瓶颈是磁盘访问,需要几毫秒,而传输速率让您传输低于每秒 100 兆字节。我不确定远程处理的实际瓶颈是什么(据我所知,性能受跨域调用数量的限制,而不是传输的数据量),但可以在应用程序域之间传输数百兆字节/秒.使用快速路径远程处理字符串可实现每秒数 GB 的传输速率。
    【解决方案3】:

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

    假设通过引用编组不是一种选择,您将不得不在某些时候进行序列化。它根本无法避免。一种方法是 Neil Barnwell 建议,使用数据库,另一种方法是使用您自己建议的本地文件。

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

    【讨论】:

    • 我还没有写代码。只是在做设计。你能告诉我有什么文章解释了使用你发布的第一种方法共享数据吗?
    • 按引用编组也会序列化数据,但会是小块。每个方法调用都会返回一些信息,有效地序列化一些数据。如果您只需要一小部分数据,这可能是一个好主意。但是,如果您必须(几乎)处理整个数据,那么与一次序列化和传输数据相比,通过许多跨域调用一点一点地获取数据将会非常慢。
    • 如果你走这条路,别忘了重写InitializeLifetimeService方法;几天前这让我发疯(“对象'...'已断开连接或在服务器上不存在。”)
    【解决方案4】:

    非常感谢您希望将此保留在内存中,但我的第一个建议是将数据写入数据库并从那里查询。远程处理仍然是一个远程调用,这是使用数据库服务器的大部分“成本”的来源,并且您必须构建事务处理以确保您不会丢失数据。如果您写入 SQL Server 数据库,则您已经准备好并等待您的事务支持,并且它对于查询来说是快-快-快。

    【讨论】:

    • 虽然使用数据库并使用已建立的技术将数据持久化并解决通信问题可能是一个好主意,但我认为事务不会是一个关键的好处。如果源应用程序域崩溃,无论是在连接到数据库的线路上还是在内存流中,数据都会丢失。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-02
    • 2011-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 2015-10-15
    相关资源
    最近更新 更多