简体   繁体   English

既然 U32 已经实现了同步,为什么还要在 Rust 中使用 AtomicU32?

[英]Why use an AtomicU32 in Rust, given that U32 already implements Sync?

The std::sync::atomic module contains a number of atomic variants of primitive types, with the stated purpose that these types are now thread-safe. std::sync::atomic模块包含许多基本类型的原子变体,目的是这些类型现在是线程安全的。 However, all the primatives that correspond to the atomic types already implement Send and Sync , and should therefore already be thread-safe.但是,对应于原子类型的所有原语都已经实现了SendSync ,因此应该已经是线程安全的。 What's the reasoning behind the Atomic types? Atomic类型背后的原因是什么?

Generally, non-atomic integers are safe to share across threads because they're immutable.通常,非原子整数可以安全地跨线程共享,因为它们是不可变的。 If you attempt to modify the value, you implicitly create a new one in most cases because they're Copy .如果您尝试修改该值,则在大多数情况下会隐式创建一个新值,因为它们是Copy However, it isn't safe to share a mutable reference to a u32 across threads (or have both mutable and immutable references to the same value), which practically means that you won't be able to modify the variable and have another thread see the results.但是,跨线程共享对u32的可变引用是不安全的(或者同时具有对相同值的可变和不可变引用),这实际上意味着您将无法修改变量并让另一个线程看到结果。 An atomic type has some additional behavior which makes it safe.原子类型具有一些使其安全的附加行为。

In the more general case, using non-atomic operations doesn't guarantee that a change made in one thread will be visible in another.在更一般的情况下,使用非原子操作并不能保证在一个线程中所做的更改将在另一个线程中可见。 Many architectures, especially RISC architectures, do not guarantee that behavior without additional instructions.许多架构,尤其是 RISC 架构,在没有额外指令的情况下不能保证这种行为。

In addition, compilers often reorder accesses to memory in functions and in some cases, across functions, and an atomic type with an appropriate barrier is required to indicate to the compiler that such behavior is not wanted.此外,编译器通常会在函数中以及在某些情况下跨函数重新排序对 memory 的访问,并且需要具有适当屏障的原子类型以向编译器指示不需要这种行为。

Finally, atomic operations are often required to logically update the contents of a variable.最后,通常需要原子操作来逻辑更新变量的内容。 For example, I may want to atomically add 1 to a variable.例如,我可能想以原子方式将 1 添加到变量中。 On a load-store architecture such as ARM, I cannot modify the contents of memory with an add instruction;在诸如 ARM 之类的加载存储体系结构上,我无法使用添加指令修改 memory 的内容; I can only perform arithmetic on registers.我只能对寄存器进行算术运算。 Consequently, an atomic add is multiple instructions, usually consisting of a load-linked, which loads a memory location, the add operation on the register, and then a store-conditional, which stores the value if the memory location has not changed.因此,原子加法是多条指令,通常由加载链接组成,它加载 memory 位置,寄存器上的加法操作,然后是存储条件,如果 memory 位置没有更改,则存储值。 There's also a loop to retry if it has.如果有的话,还有一个循环可以重试。

These are why atomic operations are needed and generally useful across languages.这就是为什么需要原子操作并且通常跨语言有用的原因。 So while one can use non-atomic operations in non-Rust languages, they don't generally produce useful results, and since one typically wants one's code to function correctly, atomic operations are desirable for correctness.因此,虽然可以在非 Rust 语言中使用非原子操作,但它们通常不会产生有用的结果,并且由于人们通常希望自己的代码正确地转换为 function,因此原子操作对于正确性来说是可取的。 Rust's atomic types guarantee this behavior by generating suitable instructions and therefore can be safely shared across threads. Rust 的原子类型通过生成合适的指令来保证这种行为,因此可以安全地跨线程共享。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM