在多个线程之间共享可变自我

时间:2015-10-16 20:24:16

标签: concurrency rust

我有一台服务器接受来自多个客户端的连接。每个客户端都可以向服务器发送消息,该消息将广播给所有其他客户端。问题是处理每个连接的函数应该具有对服务器的引用。但是,我想在单独的线程中处理连接,所以我不能直接使用引用。

由于scoped已被弃用,我尝试在self中包装Arc,但随后出现了更多问题。以下是我的尝试:

struct Server {
    listener: TcpListener,
    clients: Vec<TcpStream>
}

impl Server {

    fn new() -> Server {
        Server { 
            listener : TcpListener::bind("127.0.0.1:8085").unwrap(),
            clients : vec![] 
        }
    }

    fn handle(&self) {
        println!("test");
    }

    fn start(mut self) {
        let mut handles = vec![];
        let a : Arc<Mutex<Server>> = Arc::new(Mutex::new(self));
        let mut selfm = a.lock().unwrap();

        // cannot borrow as mutable... ?
        for stream in selfm.listener.incoming() {
            match stream {
                Ok(stream) => {
                    selfm.clients.push(stream);
                    let aa = a.clone();
                    handles.push(thread::spawn(move || {
                        aa.lock().unwrap().handle();
                    }));
                },
                Err(e) => { println!("{}", e); },
            }
        }
    }

Rust Playground

我不明白该怎么做,我担心所有这些锁都会出现死锁。你有什么建议吗?

1 个答案:

答案 0 :(得分:1)

该错误与拥有多个线程几乎无关。正如编译器所说,问题是selfm已经在行

中借用了
for stream in selfm.listener.incoming() {

所以它不能在行中可变地借用

selfm.clients.push(stream);

解决此问题的一种方法是在循环之前解构selfm,因此借用不会发生冲突。您的start方法将如下所示:

fn start(mut self) {
    let mut handles = vec![];
    let a : Arc<Mutex<Server>> = Arc::new(Mutex::new(self));
    let mut selfm = a.lock().unwrap();

     // destructure selfm here to get a reference to the listener and a mutable reference to the clients
    let Server { ref listener, ref mut clients} = *selfm;
    for stream in listener.incoming() { // listener can be used here
        match stream {
            Ok(stream) => {
                clients.push(stream); // clients can be mutated here
                let aa = a.clone();
                handles.push(thread::spawn(move || {
                    aa.lock().unwrap().handle();
                }));
            },
            Err(e) => { println!("{}", e); },
        }
    }
}

(话虽如此,您关注锁定是正确的,因为互斥锁将保持锁定状态,直到selfm超出范围,即仅在start终止时,即从不我会建议一个替代设计,但我不清楚为什么你希望线程能够访问服务器结构。)