一个简单的例子:
use std::mem;
use std::sync::{Mutex};
fn main() {
let mut orig = Mutex::new(vec![1, 2, 3]);
let mut other = Mutex::new(vec![]);
mem::swap(&mut orig, &mut other);
println!("{:?}", other);
}
据Rust说,这个程序非常安全。然而swap
(或replace
)的实现并不试图锁定任何东西。来自source:
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn swap<T>(x: &mut T, y: &mut T) {
unsafe {
// Give ourselves some scratch space to work with
let mut t: T = uninitialized();
// Perform the swap, `&mut` pointers never alias
ptr::copy_nonoverlapping(&*x, &mut t, 1);
ptr::copy_nonoverlapping(&*y, x, 1);
ptr::copy_nonoverlapping(&t, y, 1);
// y and t now point to the same thing, but we need to completely
// forget `t` because we do not want to run the destructor for `T`
// on its value, which is still owned somewhere outside this function.
forget(t);
}
}
对Mutex
或Atomic
变量使用非同步访问似乎是一个麻烦的方法,这样安全吗?如果是,为什么?
答案 0 :(得分:8)
Mutex
是Sync
,这意味着允许多个线程同时访问同一个对象。首先可以跨线程共享单个对象的类型(Arc
,还有全局变量)需要确保它们共享的对象为Sync
以确保完整性。
但是,这并不能避免Rust的其他别名规则。具体来说,在任何给定时间只能存在一个mut
的变量借位,并且它的存在必须阻止对该变量的任何其他访问。使分享成为可能的事情也必须确保这一点。 Arc
通过简单地永远不会将mut
引用分发给它的指针来实现这一点;你只能得到非mut
个引用。这就是Mutex::lock
占用&self
的原因:必须可以在非mut
引用上调用它。同样,Atomic*::store
和所有其他操作方法都需要&self
。另一方面,static mut
变量只能在不安全的代码中访问,程序员负责维护保证。
mem::swap
需要mut
个引用。当编译器允许您将两个Mutex
对象传递给swap
时,这意味着您从未与其他线程共享互斥体 - 您既不会将它们放入Arc
,也不会将它们放入static
。 mut
(不是static mut
)或swap
(除非您处于不安全的代码中)。
因此,{{1}}是安全的,因为您在一个帖子中执行此操作。
答案 1 :(得分:3)
答案很简单:可变性XOR别名。
为什么mem::swap
安全,尽管没有锁定互斥锁或执行原子操作?
因为&mut T
或T
保证在编译时没有可访问的别名。
Mutex
或AtomicXXX
在线程共享时通过限制类型和操作(AtomicXXX
)或确保&#34; Mutability XOR Aliasing&而实现安全突变的事实#34;在运行时(Mutex
)是编译时保证的一个例外,但它不会取代它。
可以安全地改变类型:
&mut T
或T
)具有特殊属性的类型按照<{1}}和mem::swap
的安全性所依赖的常规编译时规则在>>上进行,因此在情况下如果没有别名可以在编译时证明,它们可以在没有任何特定同步的情况下进行变异。