我想实现两个不同的结构,它们相互引用,可以相互变异,并且是线程安全的。
我可以介绍以下例子:
pub struct Player {
pub x: f32,
pub y: f32,
game: Option<Game>, // Reference to `Game`
}
impl Player {
pub fn new(x: f32, y: f32) -> Self {
Player { x, y, game: None}
}
// Some functions that can change `self`, `game`, and `map`
}
pub struct Game {
pub map: GameMap,
players: Vec<Player>, // Reference to `Player`
}
impl Game {
pub fn new() -> Self {
Game { map: GameMap::new(), players: Vec::new()}
}
pub fn register_player(&mut self, player: Player) {
todo!();
}
}
我想要一个类似的界面:
fn main() {
let mut p1 = Player::new(0.0, 0.0);
let mut p2 = Player::new(100.0, 100.0);
let mut game = Game::new();
game.register_player(p1);
game.register_player(p2);
p1.forward(); // changes its coordinates using `map` from `game`
p2.shoot(); // changes the `map` and possibly another player
}
我不能使用
std::rc::Rc
和std::cell::RefCell
,因为我的目标是编写线程安全代码。我尝试了std::sync::{Arc, Mutex}
,但还没有成功。我该如何解决这个挑战?
UPD在这里我添加了我尝试使用互斥体的代码
use std::sync::{Arc, Mutex};
pub struct Player {
pub x: f32,
pub y: f32,
game: Option<Arc<Mutex<Game>>>,
}
impl Player {
pub fn create(x: f32, y: f32) -> Arc<Mutex<Self>> {
let mut player = Player {
x,
y,
game: None,
};
Arc::new(Mutex::new(player))
}
pub fn mount_game(&mut self, game: Arc<Mutex<Game>>) {
self.game = Some(game);
}
}
pub struct Game {
players: Vec<Arc<Mutex<Player>>>,
}
impl Game {
pub fn create() -> Arc<Mutex<Self>> {
let mut game = Game {
players: Vec::new(),
};
Arc::new(Mutex::new(game))
}
pub fn register_player(&self, game_arc: Arc<Mutex<Self>>, player_arc: Arc<Mutex<Player>>) {
let mut game = game_arc.lock().unwrap();
game.players.push(Arc::clone(&player_arc));
player_arc.lock().unwrap().mount_game(Arc::clone(&game_arc));
}
}
fn main() {
let mut p1 = Player::create(0.0, 0.0);
let mut p2 = Player::create(0.0, 0.0);
let mut game = Game::create();
game.lock().unwrap().register_player(Arc::clone(&game), Arc::clone(&p1));
game.lock().unwrap().register_player(Arc::clone(&game), Arc::clone(&p2));
}
您发布的代码出现死锁,因为您两次锁定游戏互斥锁:
main
,当您致电game.lock()
,register_player
当你打电话给game_arc.lock()
时。此外,从 API 的角度来看,需要将游戏两次传递给
register_player
(一次作为 self
,一次作为 game_arc
)是相当尴尬的。
现在,如果我们去掉
Game
周围的互斥锁,这样我们就有了 Arc<Game>
,那么我们可以将 register_player
声明为:
pub fn register_player (self: &Arc<Game>, player_arc: Arc<Mutex<Player>>)
但是如果我们希望能够对其进行变异,我们需要将
Mutex
放入 Game
中:
pub struct Game {
players: Mutex<Vec<Arc<Mutex<Player>>>>,
// Here: ^^^^^
}
在这种情况下,
Game
包含单个字段,因此很容易,但假设您的游戏状态更复杂,那么使用起来会很痛苦。
如果我们能够告诉编译器
register_player
采用 &Arc<Mutex<Self>>
,那就太好了,但不幸的是,我们只能使用 Deref
到 Self
和 Mutex
不能使用的类型 (*)。我们也不能像这样直接在 register_player
上添加 Mutex<Game>
方法:
// Not allowed:
impl Mutex<Game> {
pub fn register_player (self: &Arc<Mutex<Game>>, player_arc: Arc<Mutex<Player>>) {
todo!();
}
}
因为
Mutex
是外国类型。然而,我们可以使用扩展特征来伪造它:
pub trait GameExt {
pub fn register_player (self: &Arc<Self>, player_arc: Arc<Mutex<Player>>);
}
impl GameExt for Mutex<Game> {
fn register_player (self: &Arc<Self>, player_arc: Arc<Mutex<Player>>) {
todo!();
}
}
最后,在
Arc<Player>
内有 Game
和在 Arc<Game>
内有 Player
是一个坏主意,因为它有引入内存泄漏的风险:即使您删除所有其他引用,它们仍然会互相引用,因此它们各自引用计数永远不会达到 0,并且永远不会被释放。可以通过用 Arc
指针替换其中一个
Weak
来避免这种情况,这会打破循环。
完整的工作示例(我还删除了一堆多余的
mut
,因为 Arc<Mutex<_>>
值在锁定之前不需要是 mut
):
use std::sync::{Arc, Mutex, Weak};
pub struct Player {
pub x: f32,
pub y: f32,
game: Option<Weak<Mutex<Game>>>,
}
impl Player {
pub fn create (x: f32, y: f32) -> Arc<Mutex<Self>> {
let player = Player {
x,
y,
game: None,
};
Arc::new (Mutex::new (player))
}
pub fn mount_game (&mut self, game: Arc<Mutex<Game>>) {
self.game = Some (Arc::downgrade (&game));
}
pub fn modify_game_state (&self) {
self.game.as_ref().unwrap().upgrade().unwrap().lock().unwrap().state = 42;
}
}
pub struct Game {
state: i32,
players: Vec<Arc<Mutex<Player>>>,
}
impl Game {
pub fn new() -> Arc<Mutex<Self>> {
let game = Game {
state: 0,
players: Vec::new(),
};
Arc::new (Mutex::new (game))
}
}
pub trait GameExt {
fn register_player (self: &Arc<Self>, player_arc: Arc<Mutex<Player>>);
}
impl GameExt for Mutex<Game> {
fn register_player (self: &Arc<Self>, player_arc: Arc<Mutex<Player>>) {
player_arc.lock().unwrap().mount_game (Arc::clone(self));
let mut game = self.lock().unwrap();
game.players.push (player_arc);
}
}
fn main() {
let p1 = Player::create (0.0, 0.0);
let p2 = Player::create (0.0, 0.0);
let game = Game::new();
game.register_player (p1); // or Arc::clone (&p1) if you need to use p1 later in this function
game.register_player (Arc::clone (&p2));
p2.lock().unwrap().modify_game_state();
}
(*)
Mutex<Foo>
不能 Deref
到 Foo
,因为在引用处于活动状态时需要将 MutexGuard
放置在某个地方,而 Deref
不提供放置防护装置的任何地方。