在 AppDomain 之间共享数据
我有一个可以有多个应用程序域的进程。每个AppDomain 都会收集一些统计数据。在指定的时间之后,我想累积这些统计数据并将它们保存到文件中。
实现此目的的一种方法是远程处理,我想避免这种情况。
我想到的唯一其他技术是将每个应用程序域的数据保存在一个文件中,并且在特定时间之后,其中一个应用程序域收集所有数据并累积它们。
但如果这一切都可以在内存中完成,而不需要序列化信息以在 AppDomain 之间传递,那就太理想了。有人有什么想法吗?
I have a process that can have multiple AppDomains. Each AppDomain collect some statistics. After a specified time, I want to accumulate these statistic and save them into a file.
One way to do this is Remoting, which I want to avoid.
The only other technique I have in mind is to save each AppDomain's data in a file, and after a specific time, one of the AppDomain collects all data and accumulate them.
But it would be ideal if this all could be done in-memory, without the cost of serializing the information to pass between AppDomains. Anyone have any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
可以在 AppDomain 之间共享数据,而无需花费编组成本。但这是一种相当hacky的方式。您可以创建一个在所有 AppDomain 之间通过引用共享的源数据对象。通过这种方式,您可以将所有数据放入一个共享对象中,而无需花费编组成本。听起来太容易了,难以置信?
首先要知道如何在不进行编组的情况下在 AppDomain 之间共享数据。为此,您可以通过 Marshal.UnsafeAddrOfPinnedArrayElement 获取数据源对象的对象地址。然后将此 IntPtr 传递给所有对此感兴趣的 AppDomain。在目标 AppDomain 中,您需要将此 IntPtr 强制转换回对象引用,这可以通过 JIT::CastAny 完成,如果您从方法返回一个对象并将其指针压入堆栈,则可以完成此操作。
Viola,您在 AppDomain 之间共享一个对象作为普通指针,并且您收到 InvalidCastExceptions。问题是,您必须为所有 AppDomain 设置 LoaderOptimization.MultiDomain,以确保定义共享数据类型的程序集作为 AppDomain 中性类型加载,该类型在所有 AppDomain 之间具有相同的方法表指针。
您可以找到一个示例应用程序,它作为 WMemoryProfiler 的一部分执行此操作。请参阅此链接以获取更多详细说明和下载链接示例代码。
基本代码是
It is possible to share data between AppDomains without the costs of Marshalling. But it is a rather hacky way. You can create a source data object which is shared by reference between all AppDomains. This way you get all data into one shared object without the costs of Marshalling. Sounds too easy to be true?
The first thing is to know how share data between AppDomains without Marshalling. For this you get the object address of your data source object via Marshal.UnsafeAddrOfPinnedArrayElement. Then you pass this IntPtr to all AppDomains which are interested in this. In the target AppDomain you need to cast this IntPtr back to an object reference which can be done JIT::CastAny which is done if you return an object from a method and push the pointer of it onto the stack.
Viola you have shared an object as plain pointer between AppDomains and you get InvalidCastExceptions. The problem is that you must set for all your AppDomains LoaderOptimization.MultiDomain to ensure that the assembly that defines the shared data type is loaded as AppDomain neutral type which has the same Method Table pointer between all AppDomains.
You can find an example application that does exactly this as part of WMemoryProfiler. See this link for a more detailed explanation and download link to the sample code.
The basic code is
避免序列化的唯一方法是使用从 MarshalByRefObject 派生的对象来表示数据,但在这种情况下,您仍然需要跨 AppDomain 边界进行编组的成本。这还可能涉及重构/重写大部分代码。
假设通过引用编组不是一个选项,您将不得不在某个时刻进行序列化。这是无法避免的。一种方法是像 Neil Barnwell 建议的那样,使用数据库,另一种方法是使用本地文件,正如您自己建议的那样。
另一种可能可行也可能不可行的方法是使用内存映射文件,具体取决于您的交付时间表和/或 .NET 4.0 采用情况,请参阅 .Net Framework 4.0:使用内存映射文件。
The only way to avoid serialisation is to represent your data using objects which derive from MarshalByRefObject, but in that case you will still have the cost of marshalling across the AppDomain boundaries. This may also involve the refactoring/re-writing of much of your code.
Assuming marshalling by reference is not an option, you will have to serialise at some point. It simply cannot be avoided. One way to do this is as Neil Barnwell suggests, with a database, another would be with a local file as you suggest yourself.
Another way which may or may not feasible depending on your delivery timeline and/or .NET 4.0 adoption, would be to use a memory mapped file, see .Net Framework 4.0: Using memory mapped files.
我倾向于说只使用远程处理。将数据写入文件也需要序列化。无论您使用什么技术,序列化似乎几乎是不可避免的。您必须使用某种通道将数据从一个应用程序域传输到另一个应用程序域,并且必须序列化数据才能通过通道获取数据。
避免序列化的唯一方法似乎是使用共享内存,以便两个应用程序域都可以访问数据而无需通过通道。即使将数据从一个应用程序域的内存深度克隆到另一个应用程序域的内存中,其核心也不过是二进制序列化(其中结果不一定存储在连续的内存位置中)。
I tend to say just use remoting. Writing the data to a file requires serialization, too. Serialization seems to be almost unavoidable what ever technology you use. You have to transfer data from one application domain to another using some channel and you will have to serialize the data in order to get it through the channel.
The only way to avoid serialization seems to be using shared memory so that both application domains can access the data without ever going through a channel. Even deep cloning the data from one application domain's memory into the other's memory is at its core nothing more then a binary serialization (where the result is not necessarily stored in consecutive memory locations).
我非常感谢您希望将其保留在内存中,但我的第一个建议是将数据写入数据库并从那里查询。远程处理仍然是远程调用,这是使用数据库服务器的大部分“成本”的来源,并且您必须构建事务处理以确保不会丢失数据。如果您写入 SQL Server 数据库,您就已经准备好了事务支持,并且查询速度非常快。
I do appreciate you want to keep this in-memory, but my first suggestion would be to write the data to a database and query from there. Remoting is still a remote call, which is where much of the "cost" of using a database server comes from, and you'd have to build in transaction-handling to make sure you don't lose data. If you write to a SQL Server database you have transaction support ready and waiting for you, and it's fast-fast-fast for queries.