Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/rust/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 当它';这不是必需的_Multithreading_Rust_Mutex - Fatal编程技术网

Multithreading 当它';这不是必需的

Multithreading 当它';这不是必需的,multithreading,rust,mutex,Multithreading,Rust,Mutex,我正在编写一个游戏,其玩家列表定义如下: pub struct PlayerList { by_name: HashMap<String, Arc<Mutex<Player>>>, by_uuid: HashMap<Uuid, Arc<Mutex<Player>>>, } NetworkServer { ... player_list: Arc<Mutex<PlayerList&

我正在编写一个游戏,其玩家列表定义如下:

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

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

虽然我正在做的唯一操作是添加到
player_列表
,但我不得不使用
Arc
而不是
HashMap
s中更自然的
Rc
,因为
互斥体
需要它。我不是从网络线程(或任何其他线程)访问播放器,因此将它们置于
互斥锁下是没有意义的。只有
HashMap
s需要锁定,我正在使用
Mutex
进行锁定。但生锈是迂腐的,它想防止一切滥用

由于我只访问主线程中的
Player
s,因此每次锁定都会很烦人,性能也会降低。是否有替代使用
不安全
或其他方法的解决方法

下面是一个例子:

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();
}
使用std::cell::cell;
使用std::collections::HashMap;
使用std::ffi::CString;
使用std::rc::rc;
使用std::sync::{Arc,Mutex};
使用std::线程;
#[派生(克隆、复制、PartialEq、Eq、散列)]
结构Uuid([u8;16]);
结构播放器{
酒吧名称:String,
发布uuid:uuid,
}
结构玩家列表{
姓名:HashMap,
作者:HashMap,
}
impl玩家列表{
fn添加玩家(&mut self,p:player){
让name=p.name.clone();
设uuid=p.uuid;
设p=Arc::new(Mutex::new(p));
self.by_name.insert(name,Arc::clone(&p));
自身按uuid插入(uuid,p);
}
}
结构网络服务器{
玩家名单:Arc,
}
impl网络服务器{
fn启动(&M自我){
让player\u list=Arc::clone(&self.player\u list);
线程::生成(移动| |{
环路{
//假网络环路
//侦听传入连接,接受播放机并将其添加到播放机列表中。
玩家列表。锁定()。展开()。添加玩家(玩家){
名称:“blahblah”.into(),
uuid:uuid([0;16]),
});
}
});
}
}
结构服务器{
玩家名单:Arc,
网络服务器:网络服务器,
}
impl服务器{
fn启动(&M自我){
self.network_server.start();
//主游戏循环
环路{
//我只在这个线程中访问这个循环中的玩家。(主线程)
//所以,虽然rust需要为单个玩家提供互斥锁,但它并不需要。
}
}
}
fn main(){
让玩家列表=弧::新(互斥体::新(玩家列表{
按名称:HashMap::new(),
按:HashMap::new(),
}));
让网络服务器=网络服务器{
玩家列表:Arc::克隆(&玩家列表),
};
让mut server=server{
球员名单,
网络服务器,
};
server.start();
}

发送
是一种标记特征,用于控制哪些对象的所有权可以跨线程边界转移。对于完全由
Send
类型组成的任何类型,它都会自动实现。这也是一个不安全的特性,因为手动实现此特性可能会导致编译器无法执行我们喜欢的关于Rust的并发安全性

问题是,
Rc
不是
Send
,因此您的
PlayerList
不是
Send
,因此无法发送到另一个线程,即使在
Arc
中包装。
unsafe
解决方法是为您的
PlayerList
结构发送
unsafe impl

将此代码放入游乐场示例中,可以使用
Arc

struct PlayerList{
姓名:HashMap,
作者:HashMap,
}
播放列表{}的不安全impl发送
impl玩家列表{
fn添加玩家(&mut self,p:player){
让name=p.name.clone();
设uuid=p.uuid;
设p=Rc::new(RefCell::new(p));
self.by_name.insert(name,Rc::clone(&p));
自身按uuid插入(uuid,p);
}
}

遗憾的是,在解释程序员在对包含
Rc
s的类型不安全地实现
Send
时必须强制执行哪些规则时,本文的内容有点少,但仅在一个线程中访问似乎足够安全

为了完整起见

由于我只在主线程中访问
玩家
,因此每次锁定玩家都很烦人,而且性能较差

你的意思是,因为现在你只在主线程中访问
玩家
,但是在以后的任何时候,你可能会意外地在另一个线程中引入对他们的访问

从语言的角度来看,如果可以获得对某个值的引用,则可以使用该值。因此,如果多个线程引用了一个值,那么从多个线程使用该值应该是安全的。在编译时,没有办法强制执行某个特定的值(虽然可以访问),但实际上从未使用过

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

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

在我看来,你有一个设计问题。如果您可以设法重新设计程序,以便只有主线程可以访问
播放器列表
,那么您将立即能够使用
Rc

例如,您可以让网络线程向主线程发送一条消息,宣布新的播放器已连接

目前,你正在“通过分享进行交流”,你可以改为“通过交流进行分享”。前者通常到处都有同步原语(如互斥体、原子等),可能会面临争用/死锁问题,而后者通常有通信队列(通道),需要“异步”
struct PlayerList {
    by_name: HashMap<String, Rc<RefCell<Player>>>,
    by_uuid: HashMap<Uuid, Rc<RefCell<Player>>>,
}

unsafe impl Send for PlayerList {}

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

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