这种方式有什么问题可以实现原子双打/长/日期/无效?

时间:2011-03-30 14:00:33

标签: c# thread-safety atomic

您无法将doublelongDateTime,任何可空或任何其他结构声明为volatile(如果可以的话,它将无效不是原子的),但在我的特殊情况下,我需要一个原子写的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;
            }
        }
    }
}

1 个答案:

答案 0 :(得分:3)

看起来你已经重新发明拳击(除了更大的类型安全性)。

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。并且代码应该假装volatile仅在这两个方法的范围内(或者创建另一个类,因此它不必假装)。

现在最后回答你的问题: - )

我同意@Eric Lippert的说法,基本锁定比双重锁定更好,除非它显示不够好。我认为双重检查锁定是可以的,只要你永远不会忘记将控制变量标记为{{1}}。我所看到的这种方法的所有问题都假设变量不是易变的。