You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust方法内创建线程时self的正确使用及连接Vec读写锁问题

解决Rust服务器中连接存储Vec的读写锁阻塞问题

你的问题核心在于监听线程长时间持有写锁:原来的代码中,你在启动监听线程时直接获取了Server的写锁,并且在整个listen函数的无限循环期间一直持有这个锁——包括listener.accept()阻塞等待新连接的阶段,导致主线程永远无法获取读锁来访问connections向量。

解决方案:仅在修改连接列表时持有写锁

调整逻辑,让写锁只在添加新连接的瞬间被持有,其余时间(包括等待新连接的阻塞阶段)释放锁权限,这样主线程就能随时获取读锁访问连接列表。

修改后的完整代码:

use std::io;
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, RwLock};
use std::thread;

pub struct Server {
    pub connections: Vec<TcpStream>,
}

impl Server {
    pub fn run(self) {
        let arc_self = Arc::new(RwLock::new(self));
        let arc_self_clone = arc_self.clone();

        // 监听线程:仅在添加连接时临时持有写锁
        let listener_thread = thread::spawn(move || {
            println!("Server is listening on port 8080");
            let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
            
            loop {
                let (stream, addr) = listener.accept().unwrap();
                println!("New client connected: {}", addr);

                // 仅在push操作时获取写锁,执行完自动释放
                let mut server = arc_self_clone.write().unwrap();
                server.connections.push(stream);
                // 写锁Guard在此处离开作用域,自动释放锁
            }
        });

        // 主线程处理输入并读取连接列表
        loop {
            let mut input = String::new();
            io::stdin().read_line(&mut input).unwrap();

            if input.trim() == "1" {
                // 现在可以正常获取读锁访问连接列表
                let server = arc_self.read().unwrap();
                println!("当前在线连接数: {}", server.connections.len());
                for (idx, _conn) in server.connections.iter().enumerate() {
                    println!("连接 {}: 已建立", idx + 1);
                }
            }
        }
    }
}

关键修改说明

  1. 移除原listen方法:将监听逻辑直接移入线程闭包,避免长时间持有Server的可变引用。
  2. 临时获取写锁:仅在server.connections.push(stream)这一步获取写锁,操作完成后锁会自动释放(因为server这个写锁Guard会离开当前作用域)。
  3. 读锁正常获取:主线程在输入"1"时,能随时获取读锁访问connections,因为写锁只在短暂的push阶段被占用。

额外注意事项

  • TcpStream是线程安全的(实现了Send和Sync),因此可以安全地存放在Arc<RwLock<Vec<TcpStream>>>中供多线程访问。
  • RwLock的读锁是共享的:多个线程可以同时持有读锁,不会互相阻塞,只有写锁会独占资源,这种设计非常适合读多写少的场景(比如服务器查看连接列表的频率远高于新连接到来的频率)。

内容的提问来源于stack exchange,提问作者redhunter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 11:10:51