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); } } } } }
关键修改说明
- 移除原
listen方法:将监听逻辑直接移入线程闭包,避免长时间持有Server的可变引用。 - 临时获取写锁:仅在
server.connections.push(stream)这一步获取写锁,操作完成后锁会自动释放(因为server这个写锁Guard会离开当前作用域)。 - 读锁正常获取:主线程在输入"1"时,能随时获取读锁访问
connections,因为写锁只在短暂的push阶段被占用。
额外注意事项
TcpStream是线程安全的(实现了Send和Sync),因此可以安全地存放在Arc<RwLock<Vec<TcpStream>>>中供多线程访问。- RwLock的读锁是共享的:多个线程可以同时持有读锁,不会互相阻塞,只有写锁会独占资源,这种设计非常适合读多写少的场景(比如服务器查看连接列表的频率远高于新连接到来的频率)。
内容的提问来源于stack exchange,提问作者redhunter
相关产品推荐
相关产品推荐

