2010-02-05 7 views
15

Ich habe einen Prozess, der mehrere AppDomains haben kann. Jede AppDomain sammelt einige Statistiken. Nach einer bestimmten Zeit möchte ich diese Statistik sammeln und sie in einer Datei speichern.Freigeben von Daten zwischen AppDomains

Eine Möglichkeit, dies zu tun, ist Remoting, die ich vermeiden möchte.

Die einzige andere Technik, die ich im Sinn habe, ist das Speichern der Daten jeder AppDomain in einer Datei, und nach einer bestimmten Zeit sammelt eine der AppDomain alle Daten und akkumuliert sie.

Aber es wäre ideal, wenn dies alles im Speicher ausgeführt werden könnte, ohne die Kosten für die Serialisierung der Informationen zwischen AppDomains zu übertragen. Hat jemand Ideen?

Antwort

12

Die einzige Möglichkeit, die Serialisierung zu vermeiden, besteht darin, Ihre Daten mit Objekten darzustellen, die von MarshalByRefObject abgeleitet sind. In diesem Fall müssen Sie jedoch die Kosten für das Marshalling über die AppDomain-Grenzen hinweg tragen. Dies kann auch das Refactoring/Neuschreiben eines Großteils Ihres Codes beinhalten.

Wenn man davon ausgeht, dass das Marshalling per Referenz keine Option ist, müssen Sie irgendwann serialisieren. Es kann einfach nicht vermieden werden. Ein Weg, dies zu tun, ist, wie Neil Barnwell vorschlägt, mit einer Datenbank, eine andere würde mit einer lokalen Datei sein, wie Sie sich selbst vorschlagen.

Eine andere Möglichkeit, die möglicherweise abhängig von Ihrer Lieferungszeitlinie und/oder .NET 4.0-Übernahme machbar ist, wäre die Verwendung einer Speicherabbilddatei, siehe .Net Framework 4.0: Using memory mapped files.

+0

Ich habe noch keinen Code geschrieben. Ich arbeite gerade an dem Design. Können Sie mir einen Artikel nennen, der die gemeinsame Nutzung von Daten mit der ersten von Ihnen geposteten Methode erläutert? – ata

+0

Marshalling by Referenz wird die Daten ebenfalls serialisieren, aber in kleinen Stücken. Jeder Methodenaufruf gibt ein bisschen Information zurück, wodurch ein Bit der Daten effektiv serialisiert wird. Dies ist wahrscheinlich eine gute Idee, wenn Sie nur einen kleinen Teil der Daten benötigen. Aber wenn Sie (fast) die gesamten Daten verarbeiten müssen, wird es bei vielen Cross-Domain-Calls Stück für Stück langsamer, verglichen mit der Serialisierung und Übertragung der Daten auf einmal. –

+2

Wenn Sie dieser Straße folgen, vergessen Sie nicht, die InitializeLifetimeService-Methode zu überschreiben; das hat mich vor ein paar Tagen verrückt gemacht ("Objekt '...' wurde getrennt oder existiert nicht auf dem Server.") –

3

Ich weiß es zu schätzen, dass Sie dies im Speicher behalten möchten, aber mein erster Vorschlag wäre, die Daten in eine Datenbank zu schreiben und von dort abzufragen. Remoting ist immer noch ein Remote-Aufruf, bei dem ein Großteil der "Kosten" für die Verwendung eines Datenbankservers anfällt, und Sie müssen eine Transaktionsverarbeitung einbauen, um sicherzustellen, dass Sie keine Daten verlieren. Wenn Sie in eine SQL Server-Datenbank schreiben, steht Ihnen Transaktionsunterstützung zur Verfügung, die auf Sie wartet. Für Abfragen ist sie schnell, schnell und schnell.

+0

Während es eine gute Idee hielten, und die Daten eine Datenbank zu verwenden und kann mit einer etablierten Technologie Kommunikationsproblem gelöst, ich glaube nicht, Transaktionen ein wesentlicher Vorteil wären . Wenn die Quellanwendungsdomänen abstürzen, gehen die Daten verloren, unabhängig davon, ob sie sich gerade auf der Verbindung zur Datenbank oder in einem Arbeitsspeicherstream befanden. –

4

Ich neige dazu zu sagen, nur Remoting zu verwenden. Das Schreiben der Daten in eine Datei erfordert ebenfalls eine Serialisierung. Serialisierung scheint fast unvermeidlich zu sein, egal welche Technologie Sie verwenden. Sie müssen Daten von einer Anwendungsdomäne zu einer anderen übertragen, indem Sie einen Kanal verwenden, und Sie müssen die Daten serialisieren, um sie durch den Kanal zu bekommen.

Die einzige Möglichkeit, Serialisierung zu vermeiden, scheint Shared Memory zu verwenden, damit beide Anwendungsdomänen auf die Daten zugreifen können, ohne jemals einen Kanal zu durchlaufen. Selbst das tiefe Klonen der Daten aus dem Speicher einer Anwendungsdomäne in den Speicher des anderen ist im Kern nichts anderes als eine binäre Serialisierung (wobei das Ergebnis nicht notwendigerweise in aufeinanderfolgenden Speicherstellen gespeichert wird).

+0

Remoting beinhaltet auch Reflection. Das ist Serialisierung + Reflexion. Auf der anderen Seite sind meine Daten nur einige lange und doppelte Werte, die ich ohne viel Aufwand in eine Datei schreiben kann. – ata

+6

Sie sehen sich die falschen Stellen an. Der Engpass bei der Verwendung einer Datei ist der Disc-Zugriff. Dieser Vorgang dauert einige Millisekunden, und die Übertragungsrate ermöglicht die Übertragung von weniger als einhundert Megabyte pro Sekunde. Ich bin nicht sicher, was der eigentliche Engpass von Remoting ist (soweit ich mich erinnere, ist die Leistung durch die Anzahl der domänenübergreifenden Aufrufe, nicht die Menge der übertragenen Daten begrenzt), aber es ist möglich, mehrere hundert Megabyte pro Sekunde zwischen den Anwendungsdomänen zu übertragen . Remoting-Strings, die den schnellen Pfad verwenden, erreichen Übertragungsraten von mehreren Gigabyte pro Sekunde. –

20

Es ist möglich, Daten zwischen AppDomains ohne die Kosten des Marshalling zu teilen. Aber es ist ein ziemlich hacky Weg. Sie können ein Quelldatenobjekt erstellen, das als Referenz zwischen allen Anwendungsdomänen verwendet wird. Auf diese Weise erhalten Sie alle Daten in einem gemeinsamen Objekt ohne die Kosten des Marshalling. Klingt zu einfach um wahr zu sein?

Die erste Sache ist zu wissen, wie Daten zwischen AppDomains ohne Marshalling teilen. Dazu erhalten Sie die Objektadresse Ihres Datenquellenobjekts über Marshal.UnsafeAddrOfPinnedArrayElement. Dann übergeben Sie diesen IntPtr an alle AppDomains, die daran interessiert sind. In der Ziel-Anwendungsdomäne müssen Sie dieses IntPtr zurück in eine Objektreferenz umwandeln, was mit JIT :: CastAny erledigt werden kann, wenn Sie ein Objekt von einer Methode zurückgeben und dessen Zeiger auf den Stapel schieben.

Viola Sie haben ein Objekt als einfacher Zeiger zwischen AppDomains geteilt und Sie erhalten InvalidCastExceptions. Das Problem ist, dass Sie für alle Ihre AppDomains LoaderOptimization festlegen müssen.MultiDomain, um sicherzustellen, dass die Assembly, die den freigegebenen Datentyp definiert, als neutraler AppDomain-Typ geladen wird, der denselben Methodentabelle-Zeiger zwischen allen AppDomains aufweist.

Sie können eine Beispielanwendung finden, die genau dies als Teil von WMemoryProfiler tut. Siehe diesen Link für eine mehr detailed explanation and download link zum Beispielcode.

Der Grundcode ist

[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

Wirklich nette Antwort. Ich war zuerst im Zweifel, ob die gemeinsame Nutzung der gleichen Objekte zwischen zwei AppDomains mit GC-Ursache von nicht gepinnten Objekten verheerenden würde (nur ein gemeinsames Objekt wird fixiert). Aber wie Sie in Ihrem Artikel schön skizzieren, gibt es nur einen GC über alle AppDomains hinweg, also klappt es. Cooles Zeug! – nitrogenycs