在不需要时强制使用互斥锁

Win*_*don 3 multithreading mutex rust

我正在写一个游戏并且玩家列表定义如下:

pub struct PlayerList {
    by_name: HashMap<String, Arc<Mutex<Player>>>,
    by_uuid: HashMap<Uuid, Arc<Mutex<Player>>>,
}
Run Code Online (Sandbox Code Playgroud)

此结构具有添加,删除,获取玩家和获取玩家数量的方法.

NetworkServerServer这股名单如下:

NetworkServer {
    ...
    player_list: Arc<Mutex<PlayerList>>,
    ...
}

Server {
    ...
    player_list: Arc<Mutex<PlayerList>>,
    ...
}
Run Code Online (Sandbox Code Playgroud)

这是Arc<Mutex>因为NetworkServer在不同的线程(网络循环)中访问列表.
当玩家加入时,会为他们生成一个线程并将它们添加到player_list中.

虽然我正在做的唯一操作就是增加player_list,但是我需要使用Arc<Mutex<Player>>而不是更自然的操作Rc<RefCell<Player>>,HashMap因为Mutex<PlayerList>需要它.我不是从网络线程(或任何其他线程)访问播放器,所以将它们置于a下是没有意义的Mutex.只HashMap需要锁定,我正在使用它Mutex<PlayerList>.但鲁斯特很迂腐,想要防止所有的误用.

因为我只是Player在主线程中访问s,所以每次执行锁定既烦人又不太高效.是否有解决方法而不是使用unsafe或什么?

这是一个例子:

use std::cell::Cell;
use std::collections::HashMap;
use std::ffi::CString;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::thread;

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct Uuid([u8; 16]);

struct Player {
    pub name: String,
    pub uuid: Uuid,
}

struct PlayerList {
    by_name: HashMap<String, Arc<Mutex<Player>>>,
    by_uuid: HashMap<Uuid, Arc<Mutex<Player>>>,
}

impl PlayerList {
    fn add_player(&mut self, p: Player) {
        let name = p.name.clone();
        let uuid = p.uuid;

        let p = Arc::new(Mutex::new(p));
        self.by_name.insert(name, Arc::clone(&p));
        self.by_uuid.insert(uuid, p);
    }
}

struct NetworkServer {
    player_list: Arc<Mutex<PlayerList>>,
}

impl NetworkServer {
    fn start(&mut self) {
        let player_list = Arc::clone(&self.player_list);
        thread::spawn(move || {
            loop {
                // fake network loop
                // listen for incoming connections, accept player and add them to player_list.
                player_list.lock().unwrap().add_player(Player {
                    name: "blahblah".into(),
                    uuid: Uuid([0; 16]),
                });
            }
        });
    }
}

struct Server {
    player_list: Arc<Mutex<PlayerList>>,
    network_server: NetworkServer,
}

impl Server {
    fn start(&mut self) {
        self.network_server.start();
        // main game loop
        loop {
            // I am only accessing players in this loop in this thread. (main thread)
            // so Mutex for individual player is not needed although rust requires it.
        }
    }
}

fn main() {
    let player_list = Arc::new(Mutex::new(PlayerList {
        by_name: HashMap::new(),
        by_uuid: HashMap::new(),
    }));
    let network_server = NetworkServer {
        player_list: Arc::clone(&player_list),
    };
    let mut server = Server {
        player_list,
        network_server,
    };
    server.start();
}
Run Code Online (Sandbox Code Playgroud)

Mat*_* M. 7

因为我只是Players在主线程中访问,所以每次锁定都是令人烦恼且性能较差的.

你的意思是,现在你只是Players在主线程中访问,但是在以后的任何时候你可能会意外地在另一个线程中引入对它们的访问权限?

从语言的角度来看,如果您可以获得对值的引用,则可以使用该值.因此,如果多个线程具有对值的引用,则应该可以安全地从多个线程使用此值.在编译时,无法强制执行特定值(尽管可访问)实际上从未使用过.

然而,这提出了一个问题:

如果给定的线程从不使用该值,为什么该线程首先可以访问它?

在我看来,你有一个设计问题.如果您可以设法重新设计程序,以便只有主线程可以访问PlayerList,那么您将立即能够使用Rc<RefCell<...>>.

例如,您可以让网络线程向主线程发送消息,宣布新玩家已连接.

目前,您正在"通过共享进行通信",您可以转而使用"通过通信共享".前者通常在整个地方都有同步原语(如互斥,原子,......),可能会遇到争用/死锁问题,而后者通常有通信队列(通道),需要"异步"风格节目.

  • @WingerSendon:通过从网络线程向主线程发送消息,通知它新玩家已连接?目前,您正在"通过共享进行通信",您可以转而使用"通过通信共享".前者通常在整个地方都有同步原语(如互斥,原子,......),可能会遇到争用/死锁问题,而后者通常有通信队列(通道),需要"异步"风格节目. (4认同)