易失性字段:我如何才能真正获得字段的最新写入值? [英] Volatile fields: How can I actually get the latest written value to a field?

查看:20
本文介绍了易失性字段:我如何才能真正获得字段的最新写入值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下示例:

private int sharedState = 0;

private void FirstThread() {
    Volatile.Write(ref sharedState, 1);
}

private void SecondThread() {
    int sharedStateSnapshot = Volatile.Read(ref sharedState);
    Console.WriteLine(sharedStateSnapshot);
}

直到最近,我的印象是,只要FirstThread()真的在SecondThread()之前执行,这个程序除了1.

Until recently, I was under the impression that, as long as FirstThread() really did execute before SecondThread(), this program could not output anything but 1.

不过,我现在的理解是:

However, my understanding now is that:

  • Volatile.Write() 发出一个释放栅栏.这意味着在将 1 分配给 sharedState 之后,不会发生前面加载或存储(按程序顺序).
  • Volatile.Read() 发出一个获取栅栏.这意味着在将 sharedState 复制到 sharedStateSnapshot 之前,不会发生后续加载或存储(按程序顺序).
  • Volatile.Write() emits a release fence. This means no preceding load or store (in program order) may happen after the assignment of 1 to sharedState.
  • Volatile.Read() emits an acquire fence. This means no subsequent load or store (in program order) may happen before the copying of sharedState to sharedStateSnapshot.

或者换一种说法:

  • sharedState 实际释放到所有处理器内核时,写入之前的所有内容也将被释放,并且,
  • 获取地址sharedStateSnapshot中的值时;sharedState 必须已经被获取.
  • When sharedState is actually released to all processor cores, everything preceding that write will also be released, and,
  • When the value in the address sharedStateSnapshot is acquired; sharedState must have been already acquired.

如果我的理解是正确的,那么没有什么可以阻止 sharedState 的获取过时",如果 FirstThread() 中的写入尚未被发布.

If my understanding is therefore correct, then there is nothing to prevent the acquisition of sharedState being 'stale', if the write in FirstThread() has not already been released.

如果这是真的,我们如何才能真正确保(假设最弱的处理器内存模型,例如 ARM 或 Alpha),程序将始终打印 1?(还是我的心智模型有误?)

If this is true, how can we actually ensure (assuming the weakest processor memory model, such as ARM or Alpha), that the program will always print 1? (Or have I made an error in my mental model somewhere?)

推荐答案

您的理解是正确的,确实无法确保程序使用这些技术始终打印 1.为确保您的程序打印 1,假设线程 2 在线程 1 之后运行,您需要在每个线程上设置两个栅栏.

Your understanding is correct, and it is true that you cannot ensure that the program will always print 1 using these techniques. To ensure your program will print 1, assuming thread 2 runs after thread one, you need two fences on each thread.

最简单的方法是使用 lock 关键字:

The easiest way to achieve that is using the lock keyword:

private int sharedState = 0;
private readonly object locker = new object();

private void FirstThread() 
{
    lock (locker)
    {
        sharedState = 1;
    }
}

private void SecondThread() 
{
    int sharedStateSnapshot;
    lock (locker)
    {
        sharedStateSnapshot = sharedState;
    }
    Console.WriteLine(sharedStateSnapshot);
}

我想引用 埃里克·利珀特:

坦率地说,我不鼓励你创建一个不稳定的领域.可变字段表明您正在做一些非常疯狂的事情:您试图在没有锁定的情况下在两个不同的线程上读取和写入相同的值.

Frankly, I discourage you from ever making a volatile field. Volatile fields are a sign that you are doing something downright crazy: you're attempting to read and write the same value on two different threads without putting a lock in place.

这同样适用于调用 Volatile.ReadVolatile.Write.事实上,它们甚至比 volatile 字段更糟糕,因为它们需要您手动执行 volatile 修饰符自动执行的操作.

The same applies to calling Volatile.Read and Volatile.Write. In fact, they are even worse than volatile fields, since they require you to do manually what the volatile modifier does automatically.

这篇关于易失性字段:我如何才能真正获得字段的最新写入值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆