阅读双重不是线程安全的吗?

时间:2022-10-22 10:38:33

Update: I just stumbled upon this in Eric Lippert's answer to another question (he is quoting the spec):

更新:我在Eric Lippert对另一个问题的回答中偶然发现了这个问题(他引用了这个规范):

Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, are not guaranteed to be atomic.

其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的。

OK, so reading a double is not atomic. This means the value could get modified mid-read, right? So how does one read a double value atomically?

好的,所以读取双精度并不是原子的。这意味着可以在读取中间修改该值,对吧?那么如何原子地读取双值?


I notice there's an Interlocked.Read method for long values. This makes sense to me, as reading a 64-bit value must require two steps and therefore be subject to race conditions just like every other non-atomic action.

我注意到有一个Interlocked.Read方法用于长值。这对我来说很有意义,因为读取64位值必须需要两个步骤,因此就像其他非原子动作一样受到竞争条件的影响。

But there's no Interlocked.Read for double values, even though System.Double is a 64-bit value.

但是没有Interlocked.Read表示双值,即使System.Double是64位值。

I am seeing some strange behavior in my program where my GUI, which displays a double in a text box while that double is also being frequently updated by other threads, is showing the correct value (in the vicinity of 200.0) most of the time, and then randomly showing an erroneous value (like -0.08) occasionally.

我在我的程序中看到一些奇怪的行为,我的GUI在文本框中显示双精度,而双倍也经常被其他线程更新,在大多数情况下显示正确的值(在200.0附近),然后偶尔随机显示一个错误值(如-0.08)。

Maybe this is a threading issue, or maybe it's something else. But first off I wanted to narrow down the possiblities. So: is reading a double thread-safe?

也许这是一个线程问题,或者可能是其他问题。但首先,我想缩小可能性。那么:正在阅读双线程安全吗?

4 个解决方案

#1


10  

The usual way: control access with a lock.

通常的方法:用锁控制访问。

#2


17  

is reading a double thread-safe?

正在阅读双线程安全?

No. As the spec says

不,正如规范所说

Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, are not guaranteed to be atomic.

其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的。

Moving on.

This means the value could get modified mid-read, right?

这意味着可以在读取中间修改该值,对吧?

Yes.

So how does one read a double value atomically?

那么如何原子地读取双值?

You take a lock out around every access to the mutable variable.

你可以在每次访问可变变量时锁定。

And a question you didn't ask, but often gets asked as a follow-up to your questions:

还有一个你没有问过的问题,但经常被问到是你问题的后续问题:

Does making a field "volatile" make reads/writes of it atomic?

使字段“易变”会使它的读/写原子化吗?

No. It is not legal to make a volatile field of type double.

不。制作double类型的volatile字段是不合法的。

#3


5  

Use Interlocked.Exchange OR Interlocked.CompareExchange for atomic read like this.

像这样使用Interlocked.Exchange或Interlocked.CompareExchange进行原子读取。

Interlocked.Exchange(ref somevariable, somevariable)

Interlocked.Exchange(ref somevariable,somevariable)

It returns original value.

它返回原始值。

If you want to avoid writing use compareExchange.

如果你想避免编写使用compareExchange。

Interlocked.CompareExchange(ref somevariable, somevalue, somevalue);

Interlocked.CompareExchange(ref somevariable,somevalue,somevalue);

This will replace the variable with the second argument if it is equal to the third argument, and return the original value. By using the same value (e.g., zero) in both spots it guarantees that the value of the variable is not changed.

如果变量等于第三个参数,它将用第二个参数替换变量,并返回原始值。通过在两个点中使用相同的值(例如,零),它保证变量的值不被改变。

#4


2  

The CLR only promises a variable alignment of 4. Which means that it is quite possible for a long or double to straddle the boundaries of a CPU cache-line. That makes the read guaranteed to be non-atomic.

CLR只允许4的变量对齐。这意味着long或double很可能跨越CPU缓存行的边界。这使得读取保证是非原子的。

It is also a fairly serious perf problem, reading such a poorly aligned variable is over 3 times as slow. Nothing you can really do about it beyond hacking pointers.

这也是一个相当严重的性能问题,读取这样一个排列不良的变量是慢3倍。除了黑客攻击之外,你无法真正做到这一点。

#1


10  

The usual way: control access with a lock.

通常的方法:用锁控制访问。

#2


17  

is reading a double thread-safe?

正在阅读双线程安全?

No. As the spec says

不,正如规范所说

Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, are not guaranteed to be atomic.

其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的。

Moving on.

This means the value could get modified mid-read, right?

这意味着可以在读取中间修改该值,对吧?

Yes.

So how does one read a double value atomically?

那么如何原子地读取双值?

You take a lock out around every access to the mutable variable.

你可以在每次访问可变变量时锁定。

And a question you didn't ask, but often gets asked as a follow-up to your questions:

还有一个你没有问过的问题,但经常被问到是你问题的后续问题:

Does making a field "volatile" make reads/writes of it atomic?

使字段“易变”会使它的读/写原子化吗?

No. It is not legal to make a volatile field of type double.

不。制作double类型的volatile字段是不合法的。

#3


5  

Use Interlocked.Exchange OR Interlocked.CompareExchange for atomic read like this.

像这样使用Interlocked.Exchange或Interlocked.CompareExchange进行原子读取。

Interlocked.Exchange(ref somevariable, somevariable)

Interlocked.Exchange(ref somevariable,somevariable)

It returns original value.

它返回原始值。

If you want to avoid writing use compareExchange.

如果你想避免编写使用compareExchange。

Interlocked.CompareExchange(ref somevariable, somevalue, somevalue);

Interlocked.CompareExchange(ref somevariable,somevalue,somevalue);

This will replace the variable with the second argument if it is equal to the third argument, and return the original value. By using the same value (e.g., zero) in both spots it guarantees that the value of the variable is not changed.

如果变量等于第三个参数,它将用第二个参数替换变量,并返回原始值。通过在两个点中使用相同的值(例如,零),它保证变量的值不被改变。

#4


2  

The CLR only promises a variable alignment of 4. Which means that it is quite possible for a long or double to straddle the boundaries of a CPU cache-line. That makes the read guaranteed to be non-atomic.

CLR只允许4的变量对齐。这意味着long或double很可能跨越CPU缓存行的边界。这使得读取保证是非原子的。

It is also a fairly serious perf problem, reading such a poorly aligned variable is over 3 times as slow. Nothing you can really do about it beyond hacking pointers.

这也是一个相当严重的性能问题,读取这样一个排列不良的变量是慢3倍。除了黑客攻击之外,你无法真正做到这一点。