为了实现线程同步,基本的思想无非是:为每个对象保留一块数据区域,这块数据区域用于这个相关对象的线程同步的锁定、判断、释放等操作。在Win32里面,通常都会用到一个“CRITICAL_SECTION”结构。
CLR实现线程同步的方式大同小异,唯一有所区别的是,为每个Object保留一块那样的区域有些浪费,因为很多Object不需要考虑线程同步,所以,CLR用了一个优化的手段。
当CLR开始进行初始化工作时(通常这时CLR刚被载入),它分配一块内存,有点类似一个数组,叫做SyncBlock Cache,这个缓存里面的每个元素就是前面说到的那个用来实现线程同步的区域(CLR里面叫SyncBlock),CLR先创建出一段这样的Cache,这样当有一个Object需要实现线程同步时,CLR就从这段Cache里面找一个空闲的SyncBlock给那个对象用。
在CLR在Heap里面创建一个对象时,会给这个对象创建两个额外的区域。第一个区域就是这个对象的MethodTablePointer,包含这个对象所有方法的指针。当我们调用一个对象的GetType()方法时,这个方法也是通过检测这个MethodTablePointer来确定这个对象的确切类型的。第二个区域就是一个索引数,指向CLR管理的SyncBlock Cache列表中的某一项。
最开始,这个索引数是一个负数,表示这个对象不需要线程同步。当CLR发现这个对象开始需要线程同步时(通常是看到了lock指令之类的),就从SyncBlock Cache中找一个空闲的SyncBlock,然后把它的索引数值放在前文所说的那个对象的第二个区域里面。当需要线程同步的代码执行完成了,这个索引数又被重新赋值为负数。