【问题标题】:Static Fields in AppDomainAppDomain 中的静态字段
【发布时间】:2011-05-16 23:34:24
【问题描述】:

我正在尝试使用 AppDomain 来管理一些在多线程环境中包含大量静态字段的遗留代码。

我读到了这个问题的答案:How to use an AppDomain to limit a static class' scope for thread-safe use?,认为它很有前途,并决定在程序集 ClassLibrary1.dll 中使用一个非常简单的类来尝试一下:

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

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

这是我的代码,它将程序集加载到 2 个不同的应用程序域并多次调用 IncrementAndPrint():

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

我期待的输出是:

0
1
2
0
1
2

因为在每个 AppDomain 实例的本地都会有一个静态字段 Value 的副本。然而,我得到的是:

0
1
2
3
4
5

这告诉我他们仍然共享静态字段值的相同副本。 谁能告诉我我在这里做错了什么?

更新:

我尝试了 Erik 的建议,现在我调用 AppDomain 类的 CreateInstanceAndUnwrap() 方法,而不是调用 Load() 和 GetType(),如下所示。此外,我已将 IncrementAndPrint 转换为实例方法而不是静态方法。但是,我仍然得到相同的结果。

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();

【问题讨论】:

  • 您正在调用当前应用程序域中的静态方法。您需要创建一个调用 Class1 类上的 Static 方法的 Instance 方法。
  • 您好 Erik,如果您查看我更新的源代码,我已将 IncrementAndPrint() 转换为实例方法,并使用 CreateInstanceAndUnWrap() 在各个应用程序域中创建实例。但是,我仍然得到相同的结果
  • 我完全忘记在您的 appDomain 模型上提及 MarhsalByRefObject

标签: c# .net appdomain


【解决方案1】:

看起来您正在将另一个 appDomain 中的类型加载到当前 appDomain 中。因此,调用静态方法的代码是从当前的 appDomain 调用的。

我不知道在另一个域中调用静态方法而不在另一个域中创建对象实例并让该对象调用静态方法的任何其他方式。

示例:解决方案包含 2 个项目(ClassLibrary 和一个 Winforms/Console 应用)

[类库]

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

[应用]

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

生成的结果:

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

【讨论】:

  • 此外,在 .NET 的原始版本中存在一个鲜为人知的错误,即在当前域中执行静态方法,然后在定义类型上调用非静态方法(在单独的域中创建)将导致在当前域中执行非静态代码(即使定义类型是 MBR)。解决方案是不对同样是 MBR 的类型调用静态方法。当时我们认为这是 JIT 优化的一个问题,他们认为 MBR 调用可以被内联,因为使用了在 MBR 上定义的静态方法。
猜你喜欢
  • 1970-01-01
  • 2013-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多