您不能将 double
、long
、DateTime
、任何可为 null 的或任何其他结构声明为 volatile
(如果可以的话它不会工作,因为写入不是原子的),但在我的特殊情况下,我需要一个 volatlie,原子编写的 DateTime?
。
我编写了这个简单的类来确保写入是原子的。如果您按如下方式获取副本,它将始终具有写入之前的值或写入之后的值,永远不会有任何不完整的值。
/// <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
。我所见过的这种方法的所有问题都假设变量不是易变的。
关于c# - 这种实现原子 double /longs/datetimes/nullables 的方法有什么问题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5487350/