我正在编写一个系统,其中我有Object
个集合,每个Object
都有一个唯一的整数ID。这是我在C ++中的表现:
class Object {
public:
Object(): id_(nextId_++) { }
private:
int id_;
static int nextId_;
}
int Object::nextId_ = 1;
这显然不是thread_safe,但如果我想要它,我可以使nextId_
成为std::atomic_int
,或者在nextId_++
表达式周围包装一个互斥量。
我如何在(最好是安全的)Rust中执行此操作?没有静态struct成员,全局可变变量也不安全。我总是可以将nextId
传递给new
函数,但是这些对象将在很多地方分配,我宁愿不管这个nextId
数字。想法?
答案 0 :(得分:8)
原子变量可以存在于静态中,因此您可以相对直接地使用它(缺点是您具有全局状态)。
示例代码:(playground link)
use std::{
sync::atomic::{AtomicUsize, Ordering},
thread,
};
static OBJECT_COUNTER: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug)]
struct Object(usize);
impl Object {
fn new() -> Self {
Object(OBJECT_COUNTER.fetch_add(1, Ordering::SeqCst))
}
}
fn main() {
let threads = (0..10)
.map(|_| thread::spawn(|| Object::new()))
.collect::<Vec<_>>();
for t in threads {
println!("{:?}", t.join().unwrap());
}
}
答案 1 :(得分:6)
全局可变变量也不安全
您的C ++示例似乎会遇到线程安全问题,但我不太了解C ++。
但是,只有不同步的全局可变变量才有问题。如果您不关心跨线程问题,可以使用线程本地:
use std::cell::Cell;
#[derive(Debug)]
struct Monster {
id: usize,
health: u8,
}
thread_local!(static MONSTER_ID: Cell<usize> = Cell::new(0));
impl Monster {
fn new(health: u8) -> Monster {
MONSTER_ID.with(|thread_id| {
let id = thread_id.get();
thread_id.set(id + 1);
Monster { id, health }
})
}
}
fn main() {
let gnome = Monster::new(41);
let troll = Monster::new(42);
println!("gnome {:?}", gnome);
println!("troll {:?}", troll);
}
如果你确实想要一些适用于多个线程的东西,请查看bluss' answer,其中显示了如何使用原子变量。