2010-11-28 15 views
28

Ich experimentiere Ideen mit AppDomain zu verwalten, einige Legacy-Code enthält viele statische Felder in einer Multi-Thread-Umgebung.Statische Felder in AppDomain

Ich lese Antworten auf diese Frage: How to use an AppDomain to limit a static class' scope for thread-safe use?, dachte, es recht vielversprechend ist und beschlossen, es mit einer sehr einfachen Klasse in der Montage ClassLibrary1.dll auszuprobieren:

namespace ClassLibrary1 
{ 
    public static class Class1 
    { 
     private static int Value = 0; 

     public static void IncrementAndPrint() 
     { 
      Console.WriteLine(Value++); 
     } 
    } 
} 

und hier ist mein Code, der die assemblyinto 2 verschiedenen Lasten App-Domains und ruft die IncrementAndPrint() mehrmals:

var appDomain1 = System.AppDomain.CreateDomain("AppDomain1"); 
var appDomain2 = System.AppDomain.CreateDomain("AppDomain2"); 

var assemblyInAppDomain1 = appDomain1.Load("ClassLibrary1"); 
var assemblyInAppDomain2 = appDomain2.Load("ClassLibrary1"); 

var class1InAppDomain1 = assemblyInAppDomain1.GetType("ClassLibrary1.Class1"); 
var class1InAppDomain2 = assemblyInAppDomain2.GetType("ClassLibrary1.Class1"); 

class1InAppDomain1.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null); 
class1InAppDomain1.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null); 
class1InAppDomain1.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null); 

class1InAppDomain2.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null); 
class1InAppDomain2.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null); 
class1InAppDomain2.InvokeMember("IncrementAndPrint", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null); 

ich die Ausgabe erwartete zu sein:

0 
1 
2 
0 
1 
2 

, da für jede Instanz von AppDomain eine Kopie des statischen Felds Value in local vorhanden ist. Stattdessen, was ich bekam, war:

was mir sagt, dass sie noch alle die gleiche Kopie des statischen Feld Wert teilen. Kann mir jemand sagen, was habe ich hier falsch gemacht?

Update:

habe ich versucht, Erik Vorschlag, jetzt nenne ich CreateInstanceAndUnwrap() -Methode der AppDomain-Klasse anstelle Last von() aufrufen und GetType(), wie unten gezeigt. Außerdem habe ich IncrementAndPrint in eine Instanzmethode und nicht in eine statische Methode konvertiert. Allerdings bekomme ich immer noch das gleiche Ergebnis.

var appDomain1 = System.AppDomain.CreateDomain("AppDomain1"); 
var appDomain2 = System.AppDomain.CreateDomain("AppDomain2"); 

var class1InAppDomain1 = (Class1)appDomain1.CreateInstanceAndUnwrap("ClassLibrary1", "ClassLibrary1.Class1"); 
var class1InAppDomain2 = (Class1)appDomain2.CreateInstanceAndUnwrap("ClassLibrary1", "ClassLibrary1.Class1"); 

class1InAppDomain1.IncrementAndPrint(); 
class1InAppDomain1.IncrementAndPrint(); 
class1InAppDomain1.IncrementAndPrint(); 

class1InAppDomain2.IncrementAndPrint(); 
class1InAppDomain2.IncrementAndPrint(); 
class1InAppDomain2.IncrementAndPrint(); 
+0

Sie rufen t Die statische Methode in der aktuellen App-Domäne. Sie müssen eine Instance-Methode erstellen, die die Static-Methode für die Class1-Klasse aufruft. –

+1

Hallo Erik, wenn Sie sich meinen aktualisierten Quellcode ansehen, habe ich IncrementAndPrint() in eine Instanzmethode konvertiert und CreateInstanceAndUnWrap() verwendet, um Instanzen in den entsprechenden App-Domänen zu erstellen. Allerdings bekomme ich immer noch das gleiche Ergebnis – oscarkuo

+0

Ich habe vergessen, das 'MarhsalByRefObject' auf Ihrem appDomain-Modell zu erwähnen. –

Antwort

21

Es sieht aus wie Sie einen Typ aus einem anderen Appdomain in die aktuelle AppDomain geladen. Daher ruft der Code, der die statischen Methoden aufruft, die aktuelle appDomain auf.

Mir ist keine andere Möglichkeit bekannt, eine statische Methode in einer anderen Domäne aufzurufen, ohne eine Instanz eines Objekts in einer anderen Domäne zu erstellen und dieses Objekt die statische Methode aufrufen zu lassen.

Beispiel: Lösung enthält 2 Projekte (Classlibrary und eine WinForms/Console app)

[Classlibrary]

using System; 

namespace MyLibrary 
{ 
    public class DomainObject : MarshalByRefObject 
    { 
     private static int _Value; 

     private static void IncrementValue() 
     { 
      DomainObject._Value++; 
     } 

     public static int Value 
     { 
      get 
      { 
       return DomainObject._Value; 
      } 
     } 

     public int GetIncrementedValue() 
     { 
      DomainObject.IncrementValue(); 
      return DomainObject.Value; 
     } 
    } 
} 

[Anwendung]

private void button1_Click(object sender, EventArgs e) 
{ 
    AppDomain domain1 = AppDomain.CreateDomain("domain1"); 
    AppDomain domain2 = AppDomain.CreateDomain("domain2"); 

    DomainObject object1 = 
     domain1.CreateInstanceAndUnwrap("MyLibrary", "MyLibrary.DomainObject") 
     as DomainObject; 

    DomainObject object2 = 
     domain2.CreateInstanceAndUnwrap("MyLibrary", "MyLibrary.DomainObject") 
     as DomainObject; 

    if (object1 != null) 
    { 
     Console.WriteLine("object 1 Value = " 
          + object1.GetIncrementedValue().ToString()); 
     Console.WriteLine("object 1 Value = " 
          + object1.GetIncrementedValue().ToString()); 
     Console.WriteLine("object 1 Value = " 
          + object1.GetIncrementedValue().ToString()); 
    } 
    if (object2 != null) 
    { 
     Console.WriteLine("object 2 Value = " 
          + object2.GetIncrementedValue().ToString()); 
     Console.WriteLine("object 2 Value = " 
          + object2.GetIncrementedValue().ToString()); 
     Console.WriteLine("object 2 Value = " 
          + object2.GetIncrementedValue().ToString()); 
    } 

    /* Unload the Domain and re-create 
    * This should reset the Static Value in the AppDomain 
    */ 
    AppDomain.Unload(domain1); 
    domain1 = AppDomain.CreateDomain("domain1"); 
    object1 = domain1.CreateInstanceAndUnwrap("MyLibrary", 
               "MyLibrary.DomainObject") 
               as DomainObject; 

    if (object1 != null) 
    { 
     Console.WriteLine("object 1 Value = " 
          + object1.GetIncrementedValue().ToString()); 
     Console.WriteLine("object 1 Value = " 
          + object1.GetIncrementedValue().ToString()); 
     Console.WriteLine("object 1 Value = " 
          + object1.GetIncrementedValue().ToString()); 
    } 
    if (object2 != null) 
    { 
     Console.WriteLine("object 2 Value = " 
          + object2.GetIncrementedValue().ToString()); 
     Console.WriteLine("object 2 Value = " 
          + object2.GetIncrementedValue().ToString()); 
     Console.WriteLine("object 2 Value = " 
          + object2.GetIncrementedValue().ToString()); 
    } 
} 

generiert Ergebnisse:

object 1 Value = 1 
object 1 Value = 2 
object 1 Value = 3 
object 2 Value = 1 
object 2 Value = 2 
object 2 Value = 3 
object 1 Value = 1 
object 1 Value = 2 
object 1 Value = 3 
object 2 Value = 4 
object 2 Value = 5 
object 2 Value = 6 
+4

Prost Mate, MarhsalByRefObject macht den Trick. – oscarkuo

+1

Zusätzlich mit der ursprünglichen Version von.NET gab es einen weniger bekannten Bug, bei dem das Ausführen einer statischen Methode in der aktuellen Domäne, die dann nicht statische Methoden auf dem definierenden Typ (in einer separaten Domäne erstellt) aufgerufen wurde, zu nicht statischem Code führte, der in der aktuellen Domäne ausgeführt wurde definierender Typ war MBR). Die Lösung bestand darin, keine statischen Methoden für Typen aufzurufen, die auch MBRs waren. Zu der Zeit, als wir glaubten, dass dies ein Problem mit JIT-Optimierungen war, glaubten MBR-Aufrufe aufgrund der Verwendung einer statischen Methode, die auf dem MBR definiert wurde, eingezeichnet zu sein. –