易失性字段:我如何才能真正获得字段的最新写入值? [英] Volatile fields: How can I actually get the latest written value to a field?
问题描述
考虑以下示例:
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
tosharedStateSnapshot
.
或者换一种说法:
- 当
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.Read
和 Volatile.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屋!