您不能将double
,long
,DateTime
,任何可为null的结构或任何其他结构声明为volatile
(如果可以的话,这是行不通的,因为写入不是原子的),但是在我的情况下我需要一个原子写的。
我写了这个简单的类来确保写是原子的。如果您按以下方式进行复制,它将始终具有写入之前的值或写入之后的值,永远不会有任何不完整的值。
/// <summary>
/// A T? where writes are atomic. Implemented as a class (which always has atomic read/writes) containing a readonly value.
/// </summary>
public class AtomicNullable<T> where T: struct {
public readonly T Value;
public AtomicNullable(T value) {
this.Value = value;
}
public static implicit operator AtomicNullable<T>(T value) {
return new AtomicNullable<T>(value);
}
}
用法:
private volatile AtomicNullable<DateTime> expiryTime = null;
private bool IsExpired() {
// Copy of expiry makes sure it doesn't get set from another thread in the middle of evaluating the boolean expression.
AtomicNullable<DateTime> expiry = this.expiryTime;
return expiry == null
|| expiry.Value < DateTime.UtcNow;
}
private void Calculate() {
if (IsExpired()) {
lock (locker) {
if (IsExpired()) {
// do calculation...
expiryTime = DateTime.UtcNow + MaximumCachedObjectAge;
}
}
}
}
最佳答案
看起来您已经重新发明了拳击(除非具有更大的类型安全性)。
private volatile object expiryTime = null;
private bool IsExpired()
{
object expiry = this.expiryTime;
return expiry == null
|| (DateTime)expiry < DateTime.UtcNow;
}
但是,类型安全性很好。
这些是我将要更改的事情:
Calculate()
应该是CalculateIfExpired()
,并且应调用Calculate()
进行实际工作。当前,
Calculate
正在设置expiryTime
字段。当不知道如何阅读expiryTime
时,为什么应该知道如何设置expiryTime
?相反,IsExpired()
应该在工具架上旁边有一个漂亮的小SetExpired()
。并且代码应假装expiryTime
仅在这两个方法的作用域内(或创建另一个类,因此不必假装)。现在终于可以回答您的问题了:-)
我同意@Eric Lippert的观点,即除非证明不够好,否则基本锁定比双重检查锁定要好。我认为,只要您不忘记将控制变量标记为
volatile
,就可以再次检查锁定。我见过的这种方法的所有问题都假定变量不是易失的。