我一直认为,如果多个线程可以访问一个变量,那么所有对该变量的读取和写入操作都必须受到同步代码的保护,例如“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必须保证对所有不超过 native 字大小的正确对齐内存位置的读写访问是原子的,而对某个位置的所有写访问都相同时。”
这样就可以确定s_Initialized永远不会不稳定,并且对小于32位的原始类型的读写是原子的。
特别是,double
和long
(Int64
和UInt64
)是,而不是,在32位平台上保证是原子的。您可以使用Interlocked
类上的方法来保护这些方法。
另外,虽然读写是原子的,但由于必须读取,操作和重写原始类型,因此存在竞争条件,其中存在加,减,递增和递减原始类型。互锁的类允许您使用CompareExchange
和Increment
方法保护它们。
互锁会形成内存屏障,以防止处理器对读取和写入进行重新排序。在此示例中,锁创建了唯一需要的屏障。