C# 在 AppDomain 之间共享数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2206961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 00:25:23  来源:igfitidea点击:

Sharing data between AppDomains

c#appdomain

提问by ata

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.

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

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.

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

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?

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

采纳答案by Adam Ralph

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.

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

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.

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

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.

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

回答by Neil Barnwell

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.

我很感激你想把它保存在内存中,但我的第一个建议是将数据写入数据库并从那里查询。远程处理仍然是远程调用,这是使用数据库服务器的大部分“成本”的来源,您必须构建事务处理以确保不会丢失数据。如果您写入 SQL Server 数据库,则事务支持已准备就绪并在等着您,而且查询速度很快。

回答by Daniel Brückner

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).

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

回答by Alois Kraus

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?

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

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.

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

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.

Viola 您在 AppDomains 之间共享了一个对象作为普通指针,并且您得到 InvalidCastExceptions。问题是您必须为所有 AppDomains LoaderOptimization.MultiDomain 设置以确保定义共享数据类型的程序集作为 AppDomain 中性类型加载,该类型在所有 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 linkto the sample code.

您可以找到作为 WMemoryProfiler 的一部分执行此操作的示例应用程序。有关更详细的说明和示例代码的下载链接,请参阅此链接。

The basic code is

基本代码是

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