【问题标题】:Why does Arc::try_unwrap() cause a panic?为什么 Arc::try_unwrap() 会引起恐慌?
【发布时间】:2019-05-10 12:37:28
【问题描述】:

我正在编写一个简单的聊天服务器,它向所有连接的客户端广播消息。

代码可能看起来很糟糕,因为我是初学者。 Peers 还没有在任何地方使用,因为我也想将它传递给handle_client 函数,所以当数据在流中可用并成功读取时,我想在所有连接的客户端上广播它。我知道这不是一个好方法,我只是想了解我通常如何做这样的事情。

use std::io::BufRead;
use std::io::Write;
use std::net::{TcpListener, TcpStream};
use std::sync::Arc;

fn handle_client(arc: Arc<TcpStream>) -> std::io::Result<()> {
    let mut stream = Arc::try_unwrap(arc).unwrap();

    stream.write(b"Welcome to the server!\r\n")?;
    println!("incomming connection: {:?}", stream);

    std::thread::spawn(move || -> std::io::Result<()> {
        let peer_addr = stream.peer_addr()?;
        let mut reader = std::io::BufReader::new(stream);
        let mut buf = String::new();

        loop {
            let bytes_read = reader.read_line(&mut buf)?;
            if bytes_read == 0 {
                println!("client disconnected {}", peer_addr);
                return Ok(());
            }

            buf.remove(bytes_read - 1);
            println!("{}: {}", peer_addr, buf);

            buf.clear();
        }
    });

    Ok(())
}

fn start() -> std::io::Result<()> {
    let listener = TcpListener::bind("0.0.0.0:1111")?;
    println!("listening on {}", listener.local_addr()?.port());

    let mut peers: Vec<Arc<TcpStream>> = vec![];

    for stream in listener.incoming() {
        let mut stream = stream.unwrap();
        let arc = Arc::new(stream);

        peers.push(arc.clone());
        handle_client(arc.clone()).unwrap();
    }

    Ok(())
}

fn main() -> std::io::Result<()> {
    start()
}

它编译得很好,但是handle_client 函数中的let mut stream = Arc::try_unwrap(arc).unwrap(); 出现了恐慌。我究竟做错了什么?为什么会恐慌?

【问题讨论】:

  • 能否调试并查看 Arc::try_unwrap(arc) 的输出?
  • 试图也为它找到一个调试器,因为我是盲目地做的(无法找到任何用于 rust 的调试器)。

标签: error-handling rust smart-pointers reference-counting


【解决方案1】:

为什么会恐慌?

您正在通过Result::Err 呼叫unwrapErr 来自 try_unwrapArc 上失败。

我做错了什么?

解开Arc移动它的价值并获得它的所有权。这失败了,因为有相同Arc 的三个克隆:

  • 主循环中的一个仍在范围内
  • peers 向量中的一个
  • 您试图在handle_client 中打开的那个。

如果 Rust 允许您解包并移动值,其他两个克隆将变为无效。 您可以使用ArcDeref 实现来借用它,而不是展开值:

let stream: &TcpStream = &arc;

由于您现在从Arc 借用值,因此您需要将arc 变量的范围移动到新线程中,否则借用检查器将无法确保它的存在时间与主题:

fn handle_client(arc: Arc<TcpStream>) -> std::io::Result<()> {
    std::thread::spawn(move || -> std::io::Result<()> {
        let mut stream: &TcpStream = &arc;
        stream.write(b"Welcome to the server!\r\n")?;

        let peer_addr = stream.peer_addr()?;
        let mut reader = std::io::BufReader::new(stream);
        let mut buf = String::new();

        // ... 
     }
}

【讨论】:

  • 好吧,如果我评论peers.push(arc.clone()),它仍然会恐慌。我发现,在删除 .clone 后它工作正常。
  • 还有 两个 其他参考资料。另一个是循环内的arc变量,其作用域是直到循环结束。当您删除 clone 时,您会移动变量,因此它在循环中不再存在。
【解决方案2】:

上面写着the documentation

返回包含的值,如果 Arc 正好有一个强 参考。

否则,将返回 Err,并与传入的 Arc 相同。

即使存在未完成的弱引用,这也会成功。

(weak reference)

您的代码可以很好地处理一个强引用和多个弱引用。

let mut peers: Vec<Weak<TcpStream>> = vec![];

for stream in listener.incoming() {
    let mut stream = stream.unwrap();
    let arc = Arc::new(stream);
    peers.push(Arc::downgrade(&arc));
    handle_client(arc).unwrap();
}

关于弱引用需要注意的一点:如果您打开一个强引用,您将无法使用弱引用。

【讨论】:

  • 感谢您的解释,我会在这方面进行更多练习。现在一切都清楚了! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-27
  • 1970-01-01
  • 2015-06-13
  • 1970-01-01
  • 2023-01-26
相关资源
最近更新 更多