2010-02-05 16 views
15

Tengo un proceso que puede tener varios AppDomains. Cada AppDomain recoge algunas estadísticas. Después de un tiempo específico, quiero acumular estas estadísticas y guardarlas en un archivo.Compartir datos entre AppDomains

Una forma de hacer esto es Remoting, que quiero evitar.

La única otra técnica que tengo en mente es guardar los datos de cada dominio de aplicación en un archivo, y después de un tiempo específico, uno de los dominios de aplicación recoge todos los datos y los acumula.

Pero sería ideal si todo esto se pudiera hacer en la memoria, sin el costo de serializar la información para pasar entre los AppDomains. ¿Alguien tiene alguna idea?

Respuesta

12

La única manera de evitar la serialización es representar sus datos utilizando objetos que se derivan de MarshalByRefObject, pero en ese caso aún tendrá que pagar el costo de coordinar los límites de AppDomain. Esto también puede implicar la refacturación/reescritura de gran parte de su código.

Suponiendo que la clasificación por referencia no es una opción, tendrá que serializar en algún momento. Simplemente no se puede evitar. Una forma de hacer esto es como sugiere Neil Barnwell, con una base de datos, otra sería con un archivo local como usted mismo sugiere.

Otra forma que puede o no ser factible dependiendo de su calendario de entrega y/o la adopción de .NET 4.0, sería utilizar un archivo de memoria asignada, consulte .Net Framework 4.0: Using memory mapped files.

+0

No he escrito el código todavía. Solo trabajando en el diseño. ¿Puede hablarme de algún artículo que explique el uso compartido de datos utilizando el primer enfoque que publicó? – ata

+0

Marshaling por referencia también serializará los datos, pero en pequeños fragmentos. Cada llamada a método devolverá un poco de información serializando efectivamente un poco de los datos. Probablemente sea una buena idea si solo necesita una pequeña porción de los datos. Pero si tiene que procesar (casi) toda la información, obtenerla poco a poco con muchas llamadas de dominio cruzado será increíblemente lenta en comparación con la serialización y la transferencia de datos a la vez. –

+2

Si sigue este camino, no olvide anular el método InitializeLifetimeService; eso me estaba volviendo loco hace unos días ("Objeto '...' ha sido desconectado o no existe en el servidor.") –

3

Aprecio que desee mantener esto en la memoria, pero mi primera sugerencia sería escribir los datos en una base de datos y consultar desde allí. Remoting es todavía una llamada remota, que es de donde proviene gran parte del "costo" de usar un servidor de base de datos, y usted tendría que desarrollar el manejo de transacciones para asegurarse de no perder datos. Si escribe en una base de datos de SQL Server, tiene el soporte de transacciones listo y esperando por usted, y es rápido y rápido para las consultas.

+0

Si bien puede ser una buena idea usar una base de datos y que los datos persistentes y resueltos a problemas de comunicación con una tecnología establecida, no creo transacciones serían un beneficio clave . Si los dominios de la aplicación de origen fallan, los datos se pierden sin importar si solo estaban en el cable de la base de datos o en un flujo de memoria. –

4

Tiendo a decir que solo uso la comunicación remota. Escribir los datos en un archivo también requiere serialización. La serialización parece ser casi inevitable, sea cual sea la tecnología que utilice. Tienes que transferir datos de un dominio de aplicación a otro usando algún canal y tendrás que serializar los datos para poder acceder a través del canal.

La única forma de evitar la serialización parece ser utilizar la memoria compartida para que ambos dominios de aplicaciones puedan acceder a los datos sin tener que pasar por un canal. Incluso la clonación profunda de los datos de la memoria de un dominio de aplicación en la memoria del otro es en esencia nada más que una serialización binaria (donde el resultado no se almacena necesariamente en ubicaciones de memoria consecutivas).

+0

Remoting también involucra a Reflection. Eso es Serialización + Reflexión. Por otro lado, mis datos son solo algunos valores largos y dobles que puedo escribir en un archivo sin mucha sobrecarga. – ata

+6

Estás mirando los lugares equivocados. El cuello de botella de usar un archivo es el acceso al disco y tomará varios milisegundos y la velocidad de transferencia le permitirá transferir menos de cien megabytes por segundo. No estoy seguro de cuál es el cuello de botella real de la comunicación remota (por lo que recuerdo, el rendimiento está limitado por el número de llamadas entre dominios, no la cantidad de datos transferidos), pero es posible transferir varios cientos de megabytes por segundo entre dominios de aplicación . Remoting Strings usando la ruta rápida logra velocidades de transferencia de varios gigabytes por segundo. –

20

Es posible compartir datos entre AppDomains sin los costos de Marshalling. Pero es una forma bastante hacky. Puede crear un objeto de datos fuente que se comparte por referencia entre todos los AppDomains. De esta forma, obtiene todos los datos en un objeto compartido sin los costos de Marshalling. Suena demasiado fácil para ser verdad?

Lo primero es saber cómo compartir datos entre AppDomains sin Marshalling. Para esto obtienes la dirección del objeto de tu fuente de datos a través de Marshal.UnsafeAddrOfPinnedArrayElement. Luego pasas este IntPtr a todos los AppDomains que estén interesados ​​en esto. En el AppDomain de destino, necesita volver a convertir este IntPtr a una referencia de objeto que se puede hacer JIT :: CastAny, que se realiza si devuelve un objeto de un método y lo inserta en la pila.

Viola ha compartido un objeto como un simple puntero entre AppDomains y obtiene InvalidCastExceptions. El problema es que debe establecer para todos sus AppDomains LoaderOptimization.MultiDomain para garantizar que el ensamblado que define el tipo de datos compartidos se cargue como tipo neutral AppDomain, que tiene el mismo puntero Tabla de métodos entre todos los AppDomains.

Puede encontrar una aplicación de ejemplo que hace exactamente esto como parte de WMemoryProfiler. Consulte este enlace para obtener un código de muestra más detailed explanation and download link.

El código básico es

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

Muy buena respuesta. La primera vez que tuve dudas fue si compartir los mismos objetos entre dos AppDomains como este causaría estragos en GC porque los objetos no fijados (solo un objeto compartido está anclado). Pero como bien delineas en tu artículo, solo hay un GC en todos los AppDomains, así que funciona. ¡Cosas interesantes! – nitrogenycs

Cuestiones relacionadas