2010-02-05 6 views
15

Ho un processo che può avere più AppDomain. Ogni AppDomain raccoglie alcune statistiche. Dopo un periodo di tempo specificato, voglio accumulare queste statistiche e salvarle in un file.Condivisione di dati tra AppDomains

Un modo per fare ciò è Remoting, che voglio evitare.

L'unica altra tecnica che ho in mente è quella di salvare i dati di ogni AppDomain in un file, e dopo un tempo specifico, uno degli AppDomain raccoglie tutti i dati e li accumula.

Ma sarebbe ideale se tutto questo potesse essere fatto in memoria, senza il costo di serializzare le informazioni da passare tra AppDomains. Qualcuno ha qualche idea?

risposta

12

L'unico modo per evitare la serializzazione è rappresentare i dati utilizzando oggetti derivati ​​da MarshalByRefObject, ma in tal caso si avrà comunque il costo di marshalling tra i confini di AppDomain. Ciò potrebbe anche comportare il refactoring/riscrittura di gran parte del tuo codice.

Supponendo che il marshalling per riferimento non sia un'opzione, è necessario eseguire la serializzazione in un determinato momento. Semplicemente non può essere evitato. Un modo per farlo è come Neil Barnwell suggerisce, con un database, un altro con un file locale come suggerito tu stesso.

Un altro modo, che può o non può a seconda fattibile sulla vostra consegna timeline e/o l'adozione di .NET 4.0, sarebbe quella di utilizzare un file di memoria mappata, vedere .Net Framework 4.0: Using memory mapped files.

+0

Non ho ancora scritto il codice. Sto solo lavorando sul design. Puoi dirmi di un articolo che spiega la condivisione dei dati utilizzando il primo approccio che hai postato? – ata

+0

Anche il marshalling per riferimento serializzerà i dati, ma in piccoli pezzi. Ogni chiamata al metodo restituirà un po 'di informazioni serializzando efficacemente un po' dei dati. Questa è probabilmente una buona idea se richiedi solo una piccola parte dei dati. Ma se devi elaborare (quasi) l'intero dato, ottenendolo a poco a poco con molte chiamate cross domain sarà incredibilmente lento rispetto alla serializzazione e al trasferimento dei dati in una sola volta. –

+2

Se segui questa strada, non dimenticare di sovrascrivere il metodo InitializeLifetimeService; questo mi stava facendo impazzire pochi giorni fa ("Oggetto" ... "è stato disconnesso o non esiste sul server.") –

3

Apprezzo che tu desideri mantenere questa memoria, ma il mio primo suggerimento sarebbe quello di scrivere i dati in un database e interrogare da lì. Il servizio remoto è ancora una chiamata remota, che è il punto da cui proviene gran parte del "costo" dell'utilizzo di un server di database, e dovresti compilare la gestione delle transazioni per assicurarti di non perdere i dati. Se si scrive su un database SQL Server, il supporto per le transazioni è pronto e in attesa di risposta ed è veloce e veloce per le query.

+0

Mentre può essere una buona idea di utilizzare un database e hanno i dati persistenti e risolto al problema di comunicazione con una tecnologia consolidata, non credo transazioni sarebbe un vantaggio chiave . Se i domini delle applicazioni di origine si arrestano in modo anomalo, i dati vengono persi, indipendentemente dal fatto che si trovino sul filo del database o in un flusso in memoria. –

4

Tendo a dire che uso solo il servizio remoto. Scrivere i dati su un file richiede anche la serializzazione. La serializzazione sembra essere quasi inevitabile quale sia la tecnologia che usi. Devi trasferire i dati da un dominio di applicazione a un altro utilizzando un canale e dovrai serializzare i dati per farlo passare attraverso il canale.

L'unico modo per evitare la serializzazione sembra utilizzare la memoria condivisa in modo che entrambi i domini applicativi possano accedere ai dati senza mai passare attraverso un canale. Perfino la clonazione profonda dei dati dalla memoria del dominio di un'applicazione alla memoria dell'altro è al suo interno nient'altro che una serializzazione binaria (in cui il risultato non è necessariamente memorizzato in posizioni di memoria consecutive).

+0

I servizi di reimpostazione coinvolgono anche Reflection. Questa è Serializzazione + Riflessione. D'altra parte i miei dati sono solo alcuni valori lunghi e doppi che posso scrivere in file senza troppo sovraccarico. – ata

+6

Stai guardando i punti sbagliati. Il collo di bottiglia dell'utilizzo di un file è l'accesso al disco e richiede diversi millisecondi e la velocità di trasferimento consente di trasferire meno di cento megabyte al secondo. Non sono sicuro di quale sia il vero collo di bottiglia del servizio remoto (a mio avviso, le prestazioni sono limitate dal numero di chiamate interdominio, non dalla quantità di dati trasferiti) ma è possibile trasferire diverse centinaia di megabyte al secondo tra domini applicativi . Le stringhe remote utilizzando il percorso veloce raggiungono velocità di trasferimento di diversi gigabyte al secondo. –

20

È possibile condividere i dati tra AppDomain senza i costi di Marshalling. Ma è un modo piuttosto hacky. È possibile creare un oggetto dati di origine condiviso per riferimento tra tutti gli AppDomain. In questo modo si ottengono tutti i dati in un unico oggetto condiviso senza i costi di Marshalling. Sembra troppo facile per essere vero?

La prima cosa è sapere come condividere i dati tra AppDomain senza Marshalling. Per questo si ottiene l'indirizzo dell'oggetto dell'oggetto sorgente dati tramite Marshal.UnsafeAddrOfPinnedArrayElement. Quindi si passa questo IntPtr a tutti gli AppDomain interessati a questo. Nell'AppDomain di destinazione è necessario eseguire il cast di questo IntPtr su un riferimento a un oggetto che può essere fatto JIT :: CastAny che viene eseguito se si restituisce un oggetto da un metodo e si spinge il puntatore di esso sullo stack.

Viola hai condiviso un oggetto come puntatore semplice tra AppDomains e ottieni InvalidCastExceptions. Il problema è che devi impostare tutti i tuoi AppDomains LoaderOptimization.MultiDomain per garantire che l'assembly che definisce il tipo di dati condiviso sia caricato come tipo neutro AppDomain che ha lo stesso puntatore del Table Table tra tutti gli AppDomain.

È possibile trovare un'applicazione di esempio che fa esattamente questo come parte di WMemoryProfiler. Vedi questo link per un ulteriore detailed explanation and download link nel codice di esempio.

Il codice di base è

[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

Davvero una bella risposta. Per prima cosa ho avuto dubbi sul fatto che condividere gli stessi oggetti tra due AppDomain come questo causerebbe il caos con GC a causa di qualsiasi oggetto non appuntato (solo un oggetto condiviso è bloccato). Ma come ben delineato nel tuo articolo c'è solo un GC su tutti gli AppDomain, quindi funziona. Roba forte! – nitrogenycs