AppDomain 中的静态字段 [英] Static Fields in AppDomain

查看:22
本文介绍了AppDomain 中的静态字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

命名空间 ClassLibrary1{公共静态类 Class1{私有静态整数值 = 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);

我期望输出是:

<预><代码>012012

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

<预><代码>012345

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

更新:

我尝试了 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();

解决方案

您似乎正在将其他 appDomain 中的类型加载到当前 appDomain 中.因此调用静态方法的代码是从当前的 appDomain 调用的.

我不知道有什么其他方法可以调用另一个域中的静态方法,而无需在另一个域中创建对象的实例,并让该对象调用静态方法.

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

[类库]

使用系统;命名空间 MyLibrary{公共类域对象:MarshalByRefObject{私有静态 int _Value;私有静态无效增量值(){DomainObject._Value++;}公共静态整数值{得到{返回域对象._值;}}public int GetIncrementedValue(){DomainObject.IncrementValue();返回域对象.值;}}}

[申请]

private void button1_Click(object sender, EventArgs e){AppDomain domain1 = AppDomain.CreateDomain("domain1");AppDomain domain2 = AppDomain.CreateDomain("domain2");域对象对象 1 =domain1.CreateInstanceAndUnwrap("MyLibrary", "MyLibrary.DomainObject")作为域对象;域对象 object2 =domain2.CreateInstanceAndUnwrap("MyLibrary", "MyLibrary.DomainObject")作为域对象;如果(对象 1 != 空){Console.WriteLine("对象 1 值 = "+ object1.GetIncrementedValue().ToString());Console.WriteLine("对象 1 值 = "+ object1.GetIncrementedValue().ToString());Console.WriteLine("对象 1 值 = "+ object1.GetIncrementedValue().ToString());}如果(对象 2 != 空){Console.WriteLine("对象 2 值 = "+ object2.GetIncrementedValue().ToString());Console.WriteLine("对象 2 值 = "+ object2.GetIncrementedValue().ToString());Console.WriteLine("对象 2 值 = "+ object2.GetIncrementedValue().ToString());}/* 卸载域并重新创建* 这应该重置 AppDomain 中的静态值*/AppDomain.Unload(domain1);domain1 = AppDomain.CreateDomain("domain1");object1 = domain1.CreateInstanceAndUnwrap("MyLibrary","MyLibrary.DomainObject")作为域对象;如果(对象 1 != 空){Console.WriteLine("对象 1 值 = "+ object1.GetIncrementedValue().ToString());Console.WriteLine("对象 1 值 = "+ object1.GetIncrementedValue().ToString());Console.WriteLine("对象 1 值 = "+ object1.GetIncrementedValue().ToString());}如果(对象 2 != 空){Console.WriteLine("对象 2 值 = "+ object2.GetIncrementedValue().ToString());Console.WriteLine("对象 2 值 = "+ object2.GetIncrementedValue().ToString());Console.WriteLine("对象 2 值 = "+ object2.GetIncrementedValue().ToString());}}

生成的结果:

对象 1 值 = 1对象 1 值 = 2对象 1 值 = 3对象 2 值 = 1对象 2 值 = 2对象 2 值 = 3对象 1 值 = 1对象 1 值 = 2对象 1 值 = 3对象 2 值 = 4对象 2 值 = 5对象 2 值 = 6

I'm experimenting ideas around using AppDomain to manage some legacy code contains lots of static fields in a multi-threaded environment.

I read answers this question: How to use an AppDomain to limit a static class' scope for thread-safe use?, thought it's quite promising and decided to try it out with a very simple class in assembly ClassLibrary1.dll:

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

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

and here's my code that loads the assemblyinto 2 different app domains and invokes the IncrementAndPrint() several times:

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

I was expecting the output to be:

0
1
2
0
1
2

because there will be a copy of the static field Value to local to each instance of AppDomain. However, instead what I got was:

0
1
2
3
4
5

which tells me they are still all sharing the same copy of the static field Value. Can anyone tell me what have I done wrong here?

Update:

I tried Erik's suggestion, now I call CreateInstanceAndUnwrap() method of the AppDomain class instead of calling Load() and GetType() as shown below. Also, I've converted IncrementAndPrint to an instance method rather than a static method. However, I'm still getting the same result.

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

解决方案

It looks like you are loading a type from another appDomain into the current appDomain. Thus the code that calls the static methods are calling from the current appDomain.

I'm unaware of any other way to call a static method in another domain without creating an instance of an object in another domain, and having that object call the static method.

Example: Solution contains 2 Projects (ClassLibrary and a 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;
        }
    }
}

[Application]

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

Generated Results:

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

这篇关于AppDomain 中的静态字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆