我一直认为,如果多个线程可以访问一个变量,那么该变量的所有读取和写入操作都必须受到同步代码的保护,例如“ lock”语句,因为处理器可能会在中途切换到另一个线程写。
但是,我正在使用Reflector浏览System.Web.Security.Membership,发现了如下代码:
public static class Membership { private static bool s_Initialized = false; private static object s_lock = new object(); private static MembershipProvider s_Provider; public static MembershipProvider Provider { get { Initialize(); return s_Provider; } } private static void Initialize() { if (s_Initialized) return; lock(s_lock) { if (s_Initialized) return; // Perform initialization... s_Initialized = true; } } }
为什么在锁之外读取s_Initialized字段?另一个线程无法尝试同时写入吗? 变量的读写是原子的吗?
对于确定的答案,请转到规格。:)
CLI规范的第I部分,第12.6.6节指出:“符合规范的CLI必须保证对所有不超过本机字大小的正确对齐内存位置的读写访问是原子的,而对某个位置的所有写访问都相同。”
这样就可以确定s_Initialized永远不会不稳定,并且对小于32位的原始类型的读写是原子的。
特别是,double和long(Int64和UInt64) 不能 保证在32位平台上是原子的。您可以使用Interlocked类上的方法来保护这些方法。
double
long
Int64
UInt64
Interlocked
另外,虽然读写是原子的,但由于必须读取,操作和重写原始类型,因此存在一种竞争条件,具有加,减,递增和递减原始类型。互锁的类使您可以使用CompareExchange和Increment方法保护它们。
CompareExchange
Increment
互锁会形成内存屏障,以防止处理器对读取和写入进行重新排序。在此示例中,锁创建了唯一需要的屏障。