当构建某个对象时,该对象的 SyncBlockIndex 会被初始化为一个负值,以表明它根本不引用任何 SyncBlock。然后,当调用一个方法以同步对该对象的访问时,CLR 会在它的缓存中查找一个可用的 SyncBlock,并且将该对象的 SyncBlockIndex 设置为引用该 SyncBlock。换言之,当对象需要同步字段时,SyncBlock 被即时与该对象相关联。当不再有其他要同步对象访问的线程时,该对象的 SyncBlockIndex 被重置为一个负数,并且 SyncBlock 可在将来用来与其他对象相关联。
有关该想法的具体表示,请参见下图。 在该图的“CLR 数据结构”部分中,您可以看到对于系统了解的每个类型,都有一个数据结构;您还可以看到 SyncBlock 结构集。在该图的“托管堆”部分中,您可以看到创建了三个对象:ObjectA、ObjectB 和 ObjectC。每个对象的 MethodTablePointer 字段都引用类型的方法表。从该方法表中,可以知道每个对象的类型。因此,我们可以容易地看到 ObjectA 和 ObjectB 是 SomeType 类型的实例,而 ObjectC 是 AnotherType 类型的实例。
您将注意到,ObjectA 的 SyncBlockIndex 开销字段被设置为 0。这表明 SyncBlock #0 当前由 ObjectA 使用。 另一方面,ObjectB 的 SyncBlockIndex 字段被设置为 -1,这表明 ObjectB 不具有与它关联以供它使用的 SyncBlock。 最后,ObjectC 的 SyncBlockIndex 字段被设置为 2,这表明它使用的是 SyncBlock #2。 在此处我已经介绍的示例中,SyncBlock #1 未使用,并且可能在将来与某个对象相关联。
因此,从逻辑上讲,您会看到堆中的每个对象都具有一个与其相关联的 SyncBlock,并且可以使用它来进行快速的独占的线程同步。但是,从物理上讲,只有在需要 SyncBlock 结构时才会将它们与对象相关联;当不再需要它们时,则会解除它们与对象之间的关联。这说明内存使用很有效。顺便说一句,如有必要,则 SyncBlock 缓存能够创建更多的 SyncBlocks,因此如果要同时对很多对象进行同步,则无需担心系统会耗尽它们。