原子操作线程安全 - 我是否需要“镜像”原子读取

本文关键字:镜像 读取 安全 线程 是否 原子操作 | 更新日期: 2023-09-27 18:33:41

以下代码是否安全(将其孤立地考虑)免受撕裂的读取?

private static double flopsErrorMargin = 0.01d;
public static double FlopsErrorMargin {
    get {
        double result = flopsErrorMargin;
        Thread.MemoryBarrier();
        return result;
    }
    set {
        Interlocked.Exchange(ref flopsErrorMargin, value);
    }
}

原子写入操作(Interlocked.Exchange())是必需的,因为不能保证在.NET平台上的单个操作中写入双精度(64位环境中的实现细节除外)。

但是,我是否还需要在读取端进行"镜像"操作? 例如,我是否仍然有被撕裂读取的风险,因为我没有原子读取值?

我的预感是我不会,因为我认为另一个内存访问(例如读取)不能与任何其他原子操作同时发生,即使其他访问本身不是原子的。但我想要一些确认!

原子操作线程安全 - 我是否需要“镜像”原子读取

不,撕裂的读取是可能的。假设您的字段访问正在读取数据并按Interlocked.Exchange交错,那么其他 32 位将是 Exchange 的更新值,从而产生撕裂读取。

对于原子读取,您需要使用Interlocked.Read(在32位机器中)。

Read 方法在 64 位系统上是不必要的,因为 64 位读取 操作已经是原子的。在 32 位系统上,64 位读取 操作不是原子操作,除非使用读取执行

这也意味着撕裂的值是可能的。

您可以定义自己的原子Read double

如下所示
public static double Read(ref double location)
{
    return Interlocked.CompareExchange(ref location, 0d, 0d);
}

这就是Interlocked.Read(long)内部实施的方式。

我是否仍然有被撕裂读取的风险,因为我没有以原子方式读取值?

是的。 Interlocked.Exchange的返回值不会被撕裂,flopsErrorMargin最终得到的值将被value(这是Interlocked.Exchange给你的两个保证),但不同步的读取访问可能会被撕裂。