有没有办法在线程之间共享数据(使用Arc
),但只允许单个线程能够改变该数据?
这样的东西在C中是可能的,但我无法在Rust中看到如何做到这一点。
Arc<Mutex<T>>
允许所有线程变异,而Arc<T>
不允许。
您可以使用类型系统以不允许突变的方式包装Arc<Mutex<T>>
,除非有一个特权所有者。这是一个例子:
use std::sync::Arc;
use std::sync::Mutex;
pub struct Writer<T>(Arc<Mutex<T>>);
impl<T> Writer<T> {
pub fn new(value: T) -> Self {
Writer(Arc::new(Mutex::new(value)))
}
pub fn reader(&self) -> Reader<T> {
Reader(Arc::clone(&self.0))
}
pub fn set(&self, value: T) {
*self.0.lock().unwrap() = value;
}
pub fn get(&self) -> T
where
T: Clone,
{
self.0.lock().unwrap().clone()
}
}
pub struct Reader<T>(Arc<Mutex<T>>);
// derive(Clone) uses incorrect bounds, so we must implement Clone manually
// (see https://stackoverflow.com/q/39415052/3650362)
impl<T> Clone for Reader<T> {
fn clone(&self) -> Self {
Reader(Arc::clone(&self.0))
}
}
impl<T> Reader<T> {
pub fn get(&self) -> T
where
T: Clone,
{
self.0.lock().unwrap().clone()
}
}
如果你把这个代码放在mod
ule中,Rust的隐私控制将证明没有用户可以复制Writer
或将Reader
变成Writer
,除非通过使用unsafe
。因此,您可以将Reader
s克隆并发送到任意数量的线程,但只将Writer
发送到应具有写访问权限的特定线程。
这种设计有许多可能的变化;例如,您可以使用RwLock
而不是Mutex
让多个读者在不写入时同时访问该值。
Playground(基于Akiner Alkan的例子)
比如C这样的东西是可能的
请注意,就像在Rust中一样,如果要在C中安全地执行此操作,则需要某种同步(互斥或类似)。 Rust坚持要明确说明如何避免数据争用。 C的不同之处在于,它只是假设你知道自己在做什么,然后在写作比赛时野蛮地惩罚你。在Rust中,惯用法是使用标准库提供的安全抽象。但是,如果你有一些其他的同步方法并且可以证明Mutex
是不必要的开销,你总是可以用C语言编写东西 - 原始指针在Rust(在unsafe
块中)和C中都基本相同。
您可以在Arc<Mutex<T>>
周围创建一个包装器,并通过setter方法使用由弧的创建者提供的键来设置值。
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
#[derive(Clone)]
pub struct CustomArc<T> {
mutable_arc: Arc<Mutex<T>>,
mutator_key: String,
}
#[derive(Clone)]
struct MyStruct {
inner_val: i32,
}
impl MyStruct {
fn set_val(&mut self, val: i32) {
self.inner_val = val;
}
fn get_val(&mut self) -> i32 {
self.inner_val.clone()
}
}
impl CustomArc<MyStruct> {
fn new(val: MyStruct, mutator_key: String) -> CustomArc<MyStruct> {
CustomArc {
mutable_arc: Arc::new(Mutex::new(val)),
mutator_key,
}
}
fn set_inner_val(&mut self, value: i32, mutator_key: String) -> Result<(), SetError> {
if mutator_key == self.mutator_key {
self.mutable_arc.lock().unwrap().set_val(value);
return Ok(());
}
Err(SetError::CouldNotSet)
}
fn get_inner_val(&self) -> i32 {
self.mutable_arc.lock().unwrap().get_val()
}
}
enum SetError {
CouldNotSet,
}
fn main() {
let my_struct = MyStruct { inner_val: 3 };
let custom_arc = CustomArc::new(my_struct, "OwnerKey".to_string());
let mut custom_arc1 = custom_arc.clone();
let mut custom_arc2 = custom_arc.clone();
let mut custom_arc3 = custom_arc.clone();
thread::spawn(move || {
println!(
"Thread1 -> Current Value: {:?}",
custom_arc1.get_inner_val()
);
if let Err(_err) = custom_arc1.set_inner_val(4, "AnotherKey".to_string()) {
println!("Could not write in thread1");
}
println!("Thread1 -> Value: {:?}", custom_arc1.get_inner_val());
});
thread::sleep_ms(500);
thread::spawn(move || {
println!(
"Thread2 -> Current Value: {:?}",
custom_arc2.get_inner_val()
);
if let Err(_err) = custom_arc2.set_inner_val(5, "OwnerKey".to_string()) {
println!("Could not write in thread2");
}
println!("Thread2 -> Value: {:?}", custom_arc2.get_inner_val());
});
thread::sleep_ms(500);
thread::spawn(move || {
println!(
"Thread3 -> Current Value: {:?}",
custom_arc3.get_inner_val()
);
if let Err(_err) = custom_arc3.set_inner_val(6, "SomeKey".to_string()) {
println!("Could not write in thread3");
}
println!("Thread3 -> Value: {:?}", custom_arc3.get_inner_val());
});
thread::sleep_ms(500);
}
由于您的CustomArc
是公共的并且mutable_arc
字段是私有的,您应该通过设置器和来自箱子外部的吸气剂访问它们。 mutator_key
的所有者(可能是另一个线程)有权改变内部数据。