Rust中线程生成后衍生函数未执行的问题排查与解决
问题
我设计的TCP服务器打算用多个线程处理流,但运行时线程里的函数完全没执行,服务器无报错直接退出。以下是最小复现代码:
use std::net::{TcpListener, TcpStream}; use std::thread; use std::env::{args, Args}; fn handle_client(mut stream: TcpStream) {} fn handle_streams(listener: &TcpListener) { for stream in listener.incoming() { match stream { Ok(stream) => { handle_client(stream); } Err(e) => { eprintln!("connecting fail, cause: {}", e); } } } } fn main() -> () { let arguments: Args = args(); let arguments_string_vector: Vec<String> = arguments.collect(); let listener_ip_address: &String = arguments_string_vector.get(1).unwrap(); let listener_port: &String = arguments_string_vector.get(2).unwrap(); let listener_address = format!("{}:{}", listener_ip_address, listener_port); let listener_bind_error_message = format!("bind fail on {}", listener_address); let listener = TcpListener::bind(listener_address.clone()) .expect(&listener_bind_error_message); println!("listening on {}", listener_address); let threads_demand_quantity_string: &String = arguments_string_vector.get(5).unwrap(); let threads_demand_quantity: u32 = threads_demand_quantity_string.parse().expect("threads demand not defined"); for _i in 1..=threads_demand_quantity { let listener_clone = listener.try_clone().unwrap(); thread::spawn( move || { handle_streams(&listener_clone); }); } }
问题出在这段代码:
for _i in 1..=threads_demand_quantity { let listener_clone = listener.try_clone().unwrap(); thread::spawn( move || { handle_streams(&listener_clone); }); }
我测试过以下类似代码能正常运行:
use std::thread; use std::time::Duration; fn main() -> () { for _i in 1..=10 { thread::spawn( move || { println!("thread"); thread::sleep(Duration::new(3, 0)) } ); } }
以及:
use std::thread; fn secondary(arg: &str) {println!("{}",arg)} fn main() -> () { for _i in 1..=10 { thread::spawn( move || { println!("thread {}", _i); secondary(&"arg") } ); } }
请问如何修复让线程里的函数正常执行?
解决方案
问题核心是主线程执行完毕后会直接退出,所有子线程会被强制终止。Rust中thread::spawn创建的是后台线程,主线程结束时不会等待这些子线程完成。你测试的两段代码看似正常,其实是因为thread::sleep让子线程存活时间足够长,主线程退出前能看到输出,但本质上主线程退出后子线程还是会被杀死。
修复方法有两种,按需选择:
方法1:让主线程等待所有子线程完成
收集每个线程的JoinHandle,在main函数末尾调用join()等待所有线程结束:
修改main函数的循环部分:
let mut handles = Vec::new(); for _i in 1..=threads_demand_quantity { let listener_clone = listener.try_clone().unwrap(); let handle = thread::spawn(move || { handle_streams(&listener_clone); }); handles.push(handle); } // 等待所有线程完成 for handle in handles { handle.join().unwrap(); }
这样主线程会一直阻塞,直到所有子线程结束(比如服务器停止监听)。
方法2:让主线程无限运行(适合服务器场景)
如果服务器需要一直运行,不需要等待子线程结束(因为子线程会一直监听连接),可以让主线程进入无限循环:
在main函数末尾添加:
// 主线程无限休眠,保持进程存活 loop { std::thread::sleep(std::time::Duration::from_secs(86400)); }
或者用更轻量的方式,比如等待标准输入:
// 等待用户输入任意内容再退出 std::io::stdin().read_line(&mut String::new()).unwrap();
两种方法都能保证子线程有机会执行handle_streams里的逻辑,不会因为主线程提前退出而被终止。
内容的提问来源于stack exchange,提问作者Dimon agon IO_04
相关产品推荐
相关产品推荐

