终身麻烦共享线程之间的引用
我有一个线程,启动工作线程,都预计永生。 每个工作线程维护它自己的Socket
列表。
有些操作要求我遍历当前活着的所有套接字,但是我在生成时尝试创build一个包含指向另一个列表所拥有的套接字的套接字的主列表。
use std::{str, thread}; use std::thread::JoinHandle; use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; use std::sync::{Arc, Mutex}; use std::ops::DerefMut; use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError}; use self::socketlist::SocketList; use self::mastersocketlist::MasterSocketList; pub struct Socket { user: String, stream: TcpStream, } mod socketlist { use self::SocketList::{Node, End}; use super::Socket; pub enum SocketList { Node(Socket, Box<SocketList>), End, } impl SocketList { pub fn new() -> SocketList { End } pub fn add(self, socket: Socket) -> SocketList { Node(socket, Box::new(self)) } pub fn newest<'a>(&'a mut self) -> Result<&'a Socket, String> { match *self { Node(ref mut socket, ref mut next) => Ok(socket), End => Err("No socket available".to_string()), } } } } mod mastersocketlist { use self::MasterSocketList::{Node, End}; use super::Socket; pub enum MasterSocketList<'a> { Node(Box<&'a Socket>, Box<MasterSocketList<'a>>), End, } impl<'a> MasterSocketList<'a> { pub fn new() -> MasterSocketList<'a> { End } pub fn add(self, socket: &'a Socket) -> MasterSocketList<'a> { MasterSocketList::Node(Box::new(&socket), Box::new(self)) } } } pub struct SlotManager { prox: JoinHandle<()>, prox_tx: Sender<TcpStream>, } impl SlotManager { pub fn new() -> SlotManager { let (tx, rx): (Sender<TcpStream>, Receiver<TcpStream>) = channel(); let tx_clone = tx.clone(); let prox = thread::spawn(move || SlotManager::event_loop(tx, rx)); SlotManager { prox: prox, prox_tx: tx_clone, } } pub fn sender(&self) -> Sender<TcpStream> { self.prox_tx.clone() } fn event_loop(tx: Sender<TcpStream>, rx: Receiver<TcpStream>) { let socket_list = Arc::new(Mutex::new(MasterSocketList::new())); let mut slot = Slot::new(socket_list.clone()); loop { match rx.try_recv() { Ok(stream) => slot.new_connection(stream), Err(e) => {} } } } } pub struct Slot { prox: JoinHandle<()>, prox_tx: Sender<TcpStream>, } impl Slot { pub fn new(master_socket_list: Arc<Mutex<MasterSocketList>>) -> Slot { let (tx, rx): (Sender<TcpStream>, Receiver<TcpStream>) = channel(); let tx_clone = tx.clone(); let prox = thread::spawn(move || Slot::event_loop(tx, rx, master_socket_list)); Slot { prox: prox, prox_tx: tx_clone, } } pub fn new_connection(&self, stream: TcpStream) { self.prox_tx.send(stream); } fn event_loop(tx: Sender<TcpStream>, rx: Receiver<TcpStream>, master_socket_list: Arc<Mutex<MasterSocketList>>) { let mut sockets = SocketList::new(); loop { // Check for new connections match rx.try_recv() { Ok(stream) => { let mut socket = Socket { user: "default".to_string(), stream: stream, }; sockets = sockets.add(socket); let mut msl_guard = match master_socket_list.lock() { Ok(guard) => guard, Err(poisoned) => poisoned.into_inner(), }; let mut msl_handle = msl_guard.deref_mut(); *msl_handle = msl_handle.add(sockets.newest().unwrap()); } Err(e) => {} } } } } fn main() { let mut slot_manager = SlotManager::new(); let listener = TcpListener::bind("127.0.0.1:1234").unwrap(); for stream in listener.incoming() { match stream { Ok(stream) => { let sender = slot_manager.sender(); thread::spawn(move || { sender.send(stream); //process_new_connection(stream, sender) }); } Err(e) => println!("Connection error: {}", e), } } drop(listener); }
我收到的错误…
error[E0477]: the type `[closure@src/main.rs:107:34: 107:86 tx:std::sync::mpsc::Sender<std::net::TcpStream>, rx:std::sync::mpsc::Receiver<std::net::TcpStream>, master_socket_list:std::sync::Arc<std::sync::Mutex<mastersocketlist::MasterSocketList<'_>>>]` does not fulfill the required lifetime --> src/main.rs:107:20 | 107 | let prox = thread::spawn(move || Slot::event_loop(tx, rx, master_socket_list)); | ^^^^^^^^^^^^^ | = note: type must outlive the static lifetime
我甚至不知道我所尝试的是否可能作为安全的代码。
我希望mastersocketlist
包含一个指向套接字的生命周期由创build它的线程定义的套接字的指针。 我相信这就是所有这些错误的意思,但我不知道如何提供适当的生命周期注释来解决它。
关于Rust的一个很棒的事情是跨函数的types检查完全是通过函数签名来完成的。 这意味着你可以用unimplemented!()
replace大部分函数体,并保留types检查错误。
重复这个过程几次,你最终不会调用很多的function – 删除这些。 内联模块和减less结构/枚举也可以提供帮助。
在某个时候,你的错误将会消失 – 解决问题的第一步。 坚持下去,你会得到一个微小的复制:
use std::thread; use std::sync::{Arc, Mutex}; pub enum MasterSocketList<'a> { One(&'a u8), } pub struct Slot; impl Slot { pub fn new<'a>(master_socket_list: Arc<Mutex<MasterSocketList<'a>>>) -> Slot { thread::spawn(move || { master_socket_list; }); unimplemented!(); } } fn main() {}
检查出错,它仍然匹配:
error[E0477]: the type `[closure@src/main.rs:12:23: 12:54 master_socket_list:std::sync::Arc<std::sync::Mutex<MasterSocketList<'a>>>]` does not fulfill the required lifetime --> src/main.rs:12:9 | 12 | thread::spawn(move || { master_socket_list; }); | ^^^^^^^^^^^^^ | = note: type must outlive the static lifetime
让我们检查一下thread::spawn
签名的文档:
pub fn spawn<F>(f: F) -> JoinHandle where F: FnOnce(), F: Send + 'static
这里的关键是F: Send + 'static
– 你给出的封闭只能包含持续整个程序生命周期的引用 。 这是因为spawn
可以创build分离的线程。 一旦脱离,线程可以永远活着,所以所有的引用必须至less生活那么久,否则你会得到悬而未决的引用 ,一件坏事! 铁锈再次拯救了一天!
如果要确保线程将在已知点处终止,则可以使用有限范围的线程 ,例如由scoped-threadpool或crossbeam提供的线程 。
如果你的代码里面没有一个具有生命周期的variables,那么使用一些像Arc
这样的共享所有权,并且确保只有一个线程可以改变这个variables,像Mutex
就足够了。 这允许每个线程拥有共享的值,最后只要最后一个线程退出就丢弃它。 有关详细信息,请参阅在线程间共享可变对象