根据我的了解,我应该始终选择Arc<T>
来进行跨线程的共享读访问,选择Arc<Mutex<T>>
来进行跨线程的共享写访问。但是是否有情况下我不想使用Arc<T>
/Arc<Mutex<T>>
而是想采用完全不同的方式?例如像这样:
unsafe impl Sync for MyStruct {}
unsafe impl Send for MyStruct {}
let shared_data_for_writing = Arc::from(MyStruct::new());
根据我的了解,我应该始终选择Arc<T>
来进行跨线程的共享读访问,选择Arc<Mutex<T>>
来进行跨线程的共享写访问。但是是否有情况下我不想使用Arc<T>
/Arc<Mutex<T>>
而是想采用完全不同的方式?例如像这样:
unsafe impl Sync for MyStruct {}
unsafe impl Send for MyStruct {}
let shared_data_for_writing = Arc::from(MyStruct::new());
除了使用 Arc<T>
,我们还可以使用作用域线程(scoped threads)在线程之间共享对象,例如使用 crossbeam::scope
和 Scope::spawn
。作用域线程允许我们将借用指针 (&'a T
) 发送到在作用域中生成的线程中。作用域保证了线程在引用项被丢弃之前终止。与 Arc<T>
相比,借用指针没有运行时开销(Arc<T>
需要更多的内存,并需要使用原子指令来维护引用计数器)。
Mutex<T>
是确保最多只有一个线程可以同时变异值的最基本通用包装器。 Mutex<T>
有一个缺点:如果有许多只想读取互斥锁中的值的线程,则它们不能同时执行,即使这样做是安全的。 RwLock<T>
通过允许多个并发读取器(同时仍然确保编写器具有独占访问权)来解决这个问题。
像 AtomicUsize
这样的原子类型也允许跨线程变异,但仅适用于小值(8、16、32或64位,一些处理器支持对128位值进行原子操作,但标准库尚未公开该操作;请参见 atomic::Atomic
)。例如,您可以使用 Arc<AtomicUsize>
而不是 Arc<Mutex<usize>>
。原子类型不需要锁定,但它们通过原子机器指令进行操作。原子指令集与非原子指令集略有不同,因此从非原子类型切换到原子类型可能并不总是“即插即用”的替代品。
Arc<RwLock<T>>
而不是Arc<Mutex<T>>
。 - Alex Huszagh