小值
对于小值(基本上可以声明为 volatile 的任何字段),您可以执行以下操作:
private static volatile int backingField;
public static int Field
{
get { return backingField; }
set { backingField = value; }
}
大值
对于较大的值,如果值大于 32 位计算机上的 32 位或 64 位计算机上的 64 位,则分配将不是原子的。请参阅ECMA 335 12.6.6 规范。所以对于引用类型和大多数内置值类型,赋值是原子的,但是如果你有一些大的结构,比如:
struct BigStruct
{
public long value1, valuea0a, valuea0b, valuea0c, valuea0d, valuea0e;
public long value2, valuea0f, valuea0g, valuea0h, valuea0i, valuea0j;
public long value3;
}
在这种情况下,您需要对 get 访问器进行某种锁定。您可以为此使用ReaderWriterLockSlim,我在下面演示了这一点。 Joe Duffy 有 advice 使用 ReaderWriterLockSlim 与 ReaderWriterLock:
private static BigStruct notSafeField;
private static readonly ReaderWriterLockSlim slimLock =
new ReaderWriterLockSlim();
public static BigStruct Safe
{
get
{
slimLock.EnterReadLock();
var returnValue = notSafeField;
slimLock.ExitReadLock();
return returnValue;
}
set
{
slimLock.EnterWriteLock();
notSafeField = value;
slimLock.ExitWriteLock();
}
}
不安全的 Get-Accessor 演示
这是我用来显示在 get-accessor 中不使用锁时缺乏原子性的代码:
private static readonly object mutexLock = new object();
private static BigStruct notSafeField;
public static BigStruct NotSafe
{
get
{
// this operation is not atomic and not safe
return notSafeField;
}
set
{
lock (mutexLock)
{
notSafeField = value;
}
}
}
public static void Main(string[] args)
{
var t = new Thread(() =>
{
while (true)
{
var current = NotSafe;
if (current.value2 != (current.value1 * 2)
|| current.value3 != (current.value1 * 5))
{
throw new Exception(String.Format("{0},{1},{2}", current.value1, current.value2, current.value3));
}
}
});
t.Start();
for(int i=0; i<50; ++i)
{
var w = new Thread((state) =>
{
while(true)
{
var index = (int) state;
var newvalue = new BigStruct();
newvalue.value1 = index;
newvalue.value2 = index * 2;
newvalue.value3 = index * 5;
NotSafe = newvalue;
}
});
w.Start(i);
}
Console.ReadLine();
}