在Go语言中,变量赋值是原子操作吗?

huangapple go评论81阅读模式
英文:

Is variable assignment atomic in go?

问题

如果我有两个线程同时修改结构体上的字符串字段,我是否总是只能看到其中一个字符串被分配给该字段,而不会有其他情况发生?

英文:

If I have two threads concurrently modifying a string field on a struct, will I always see one or the other string assigned to the field, but nothing else?

答案1

得分: 20

不需要。如果你需要原子操作,可以使用sync/atomic

Go内存模型中包含了所有相关的细节。从内存模型文档的开头可以看到:

> 同时被多个goroutine访问的数据,程序必须对这些访问进行序列化。
>
> 要进行序列化访问,可以使用通道操作或其他同步原语,比如syncsync/atomic包中的原语。

英文:

No. If you need atomic operations, there is sync/atomic.

The Go Memory Model will have all the related details. From the top of the Memory Model document:

> Programs that modify data being simultaneously accessed by multiple
> goroutines must serialize such access.
>
> To serialize access, protect the data with channel operations or other
> synchronization primitives such as those in the sync and sync/atomic
> packages.

答案2

得分: 4

截至今天,2022年6月6日的Go内存模型保证了对于不超过机器字大小的内存访问是原子的。

否则,对于一个不超过机器字大小的内存位置x的读取r,必须观察到某个写入w,使得r不发生在w之前,并且不存在写入w'使得w发生在w'之前且w'发生在r之前。也就是说,每次读取必须观察到由前一个或并发的写入所写入的值。

然而,字符串的大小肯定大于机器字,所以不能保证你的访问是原子的。在这种情况下,结果是未指定的,但最有可能是来自不同写入的不同部分的交错。

对于大于单个机器字大小的内存位置的读取,鼓励但不要求满足与字大小内存位置相同的语义,观察到单个允许的写入w。出于性能原因,实现可能将较大的操作视为一组以未指定顺序的单个机器字大小操作。这意味着对于多字数据结构的竞争可能导致不一致的值,这些值不对应于单个写入。当这些值依赖于内部(指针、长度)或(指针、类型)对的一致性时,这可能是接口值、映射、切片和字符串在大多数Go实现中的情况,这样的竞争反过来可能导致任意的内存损坏。

请注意,sync/atomic不仅提供原子性,还提供顺序一致性。因此,对于不超过机器字大小的访问,sync/atomic只提供额外的顺序一致性。

sync/atomic包中的API是可以用于同步不同goroutine执行的“原子操作”。如果原子操作A的效果被原子操作B观察到,则A在B之前同步。在程序中执行的所有原子操作都表现得好像按照某个顺序一致的顺序执行。

前面的定义与C++的顺序一致原子操作和Java的volatile变量具有相同的语义。

英文:

As of today, the version on June 6, 2022 of the Go memory model guarantees that a memory access not larger than a machine word is atomic.
> Otherwise, a read r of a memory location x that is not larger than a machine word must observe some write w such that r does not happen before w and there is no write w' such that w happens before w' and w' happens before r. That is, each read must observe a value written by a preceding or concurrent write.

However, a string is definitely larger than a machine word, so your accesses are not guaranteed to be atomic. In this case, the result is unspecified, but it is most likely to be an interleave of different parts from different writes.

> Reads of memory locations larger than a single machine word are encouraged but not required to meet the same semantics as word-sized memory locations, observing a single allowed write w. For performance reasons, implementations may instead treat larger operations as a set of individual machine-word-sized operations in an unspecified order. This means that races on multiword data structures can lead to inconsistent values not corresponding to a single write. When the values depend on the consistency of internal (pointer, length) or (pointer, type) pairs, as can be the case for interface values, maps, slices, and strings in most Go implementations, such races can in turn lead to arbitrary memory corruption.

Note that sync/atomic provides not only atomicity, but also sequential consistency. As a result, for accesses not larger than machine word, sync/atomic only provides additional sequential consistency.

> The APIs in the sync/atomic package are collectively “atomic operations” that can be used to synchronize the execution of different goroutines. If the effect of an atomic operation A is observed by atomic operation B, then A is synchronized before B. All the atomic operations executed in a program behave as though executed in some sequentially consistent order.<br><br>
The preceding definition has the same semantics as C++’s sequentially consistent atomics and Java’s volatile variables.

huangapple
  • 本文由 发表于 2016年1月13日 01:39:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/34750323.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定