技术开发 频道

深入理解String 高效使用精妙设计

  我们来简单地分析一下上面的coding.

  我们创建了一个继承自MarshalByRefObject,因为我需要让它具有跨AppDomain传递的能力。在这个Class中定义了两个为实现线程同步的helper字段,一个是string类型的_stringLockHelper和object类型的_objectLockHelper,并为他们定义了相应的Property。此外定义了两个方法:ExecuteWithStringLocked和ExecuteWithStringLocked,他们的操作类似:在先对_stringLockHelper和_objectLockHelper加锁的前提下,输出出操作执行的AppDomain和确切时间。我们通过调用Thread.Sleep模拟10s的时间延迟。

  在Main方法中,首先创建了两个AppDomain,名称分别为Artech.AppDomain1和Artech.AppDomain2。随后在这两个AppDomain中创建两个MarshalByRefType对象,并为它们的StringLockHelper属性赋上相同的值:Hello World。最后,我们创建了两个新的线程,并在它们中分别调用在两个不同AppDomain 中创建的MarshalByRefType对象的ExecuteWithStringLocked方法。我们来看看运行后的输出结果:

1
 

  从上面的输出结果中可以看出,两个分别在不同线程中执行操作对应的AppDomain的name分别为Artech.AppDomain1和Artech.AppDomain2。执行的时间(确切地说是操作成功地对MarshalByRefType对象的_stringLockHelper字段进行加锁的时间)相隔10s,也就是我们在程序中定义的时间延迟。

  为什么会出现这样的结果呢?我们只是对两个处于不同AppDomain的不同的MarshalByRefType对象的stringLockHelper字段进行加锁。由于我们是同时开始他们对应的线程,照理说它们之间不会有什么关联,显示出来的时间应该是相同的。唯一的解释就是:虽然这两个在不同的AppDomain中创建的对象是两个完全不同的对象,由于他们的stringLockHelper字段具有相同的字符序列,它们引用的是同一个string。这就证明了我们提出的跨AppDomain进行string interning的结论。

  为了进一步印证我们的结论,我们是使两个MarshalByRefObject对象的stringLockHelper字段具有不同的值,看看结果又如何。于是我们把其中一个对象的stringLockHelper字段改为”Hello World!”(多加了一个!) 。

marshalByRefObj1.StringLockHelper = "Hello World";
marshalByRefObj2.StringLockHelper
= "Hello World!";

 

  看看现在的输出结果,现在的时间是一样了。

1
 

  上面我们做的是对string类型字段加锁的试验。那么我们对其他类型的对象进行加锁,又会出现怎么的情况呢?我们现在就来做这样试验:在各自的线程中调用两个对象的ExecuteWithObjectLocked方法。我们修改Execute方法和Main()。

static void Execute(object obj)
        {
            MarshalByRefType marshalByRefObj
= obj as MarshalByRefType;
            marshalByRefObj. ExecuteWithObjectLocked ();
static void Main(string[] args)
        {
            AppDomain appDomain1
= AppDomain.CreateDomain("Artech.AppDomain1");
            AppDomain appDomain2
= AppDomain.CreateDomain("Artech.AppDomain2");

            MarshalByRefType marshalByRefObj1
= appDomain1.CreateInstanceAndUnwrap("Artech.ImmutableString", "Artech.ImmutableString.MarshalByRefType") as MarshalByRefType;
            MarshalByRefType marshalByRefObj2
= appDomain2.CreateInstanceAndUnwrap("Artech.ImmutableString", "Artech.ImmutableString.MarshalByRefType") as MarshalByRefType;

            
object obj = new object();
            marshalByRefObj1.ObjectLockHelper
= obj;
            marshalByRefObj2.ObjectLockHelper
= obj;

            Thread thread1
= new Thread(new ParameterizedThreadStart(Execute));
            Thread thread2
= new Thread(new ParameterizedThreadStart(Execute));

            thread1.Start(marshalByRefObj1);
            thread2.Start(marshalByRefObj2);

            Console.Read();            
        }


  我们先来看看运行后的输出结果:

1
 

  我们发现两个时间是一样的,那么就是说两个对象的ObjectLockHelper引用的不是同一个对象。虽然上面的程序很简单,我觉得里面涉及的规程却很值得一说。我们来分析下面3段代码。

object obj = new object();
marshalByRefObj1.ObjectLockHelper
= obj;
marshalByRefObj2.ObjectLockHelper
= obj;

 
  简单看起来,两个MarshalByRefObject对象的ObjectLockHelper都是引用的同一个对象obj。但是背后的情况没有那么简单。代码第一行创建了一个新的对象obj,这个对象是在当前AppDomain 中创建的。二对于当前的AppDomain来说,marshalByRefObj1和marshalByRefObj2仅仅是一个Transparent proxy而已,它们包含一个在Artech.AppDomain1和Artech.AppDomain2中创立的MarshalByRefObject对象的引用。我们为它的ObjectLockHelper复制,对于Transparent proxy对象的赋值调用会传到真正对象所在的AppDomain,由于obj是当前AppDomain的对象,它不能直接赋给另一个AppDomain的对象。所以它必须经历一个Marshaling的过程才能被传递到另外一个AppDomain。实际上当复制操作完成之后,真正的ObjectLockHelper属性对应的对象是根据原数据重建的对象,和在当前AppDomain中的对象已经没有任何的关系。所以两个MarshalByRefObject对象的ObjectLockHelper属性引用的并不是同一个对象,所以对它进行加锁对彼此不要产生任何影响。  

0
相关文章