c# - 这种实现原子 double /longs/datetimes/nullables 的方法有什么问题吗?

标签 c# thread-safety atomic

您不能将 doublelongDateTime、任何可为 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/

相关文章:

c# - 在 C# 中解析原始 Protocol Buffer 字节流

php - 安装 php5-dev Ubuntu 16.04

java - 为什么ThreadLocal不在新线程中创建?

c++ - 将多个值打包成一个原子的标准化方法

c++ - 错误: use of deleted function ‘std::atomic<_Tp>::atomic() [with _Tp = node]’

c - 使用 32 位读取以原子方式读取 4 个字节的数组

c# - LINQ select 语句后的强类型新对象

c# - 从文字中提取控制

c# - 我应该如何缓冲绘制的矩形以提高性能(C#/.NET/WinForms/GDI+)

c++ 多线程参数问题