如何在Rust中控制两个不同线程的打印顺序?
多线程输入输出同步问题解决
我写了个小 Rust 程序,开了两个线程:sender 线程负责提示用户输入,receiver 线程接收输入内容并打印到标准输出。
预期运行效果
> hello < hello
实际运行效果
> hello > < hello
问题代码
use std::io; use std::io::Write; use std::thread; use std::sync::mpsc; fn main() { let mut threads = vec![]; let (tx, rx) = mpsc::sync_channel(1); // Sender threads.push(thread::spawn(move || { loop { let mut input = String::new(); print!("> "); io::stdout().flush().unwrap(); io::stdin().read_line(&mut input).unwrap(); tx.send(input).unwrap(); } })); // Receiver threads.push(thread::spawn(move || { loop { let message = rx.recv().unwrap(); println!("< {message}"); } })); for thread in threads { thread.join().unwrap(); } }
我猜测是 receiver 执行println!("< {message}")时,sender 已经走到下一个print!("> "),导致两者输出被错误地混在一起刷新。
解决方案
问题核心是标准输出的操作没有线程同步,两个线程的输出操作并发执行,导致输出顺序混乱。解决思路是给标准输出加全局互斥锁,确保同一时间只有一个线程能操作 stdout。
修改后的代码
use std::io; use std::io::Write; use std::thread; use std::sync::{mpsc, Mutex}; use std::sync::Arc; fn main() { let mut threads = vec![]; let (tx, rx) = mpsc::sync_channel(1); // 创建Arc包装的Mutex,实现跨线程共享的互斥访问 let stdout_mutex = Arc::new(Mutex::new(io::stdout())); // Sender线程 let sender_stdout = Arc::clone(&stdout_mutex); threads.push(thread::spawn(move || { loop { let mut input = String::new(); // 获取锁后操作stdout let mut stdout = sender_stdout.lock().unwrap(); print!("> "); stdout.flush().unwrap(); // 立即释放锁,避免等待输入时占用锁资源 drop(stdout); io::stdin().read_line(&mut input).unwrap(); tx.send(input).unwrap(); } })); // Receiver线程 let receiver_stdout = Arc::clone(&stdout_mutex); threads.push(thread::spawn(move || { loop { let message = rx.recv().unwrap(); // 获取锁后执行打印操作 let mut stdout = receiver_stdout.lock().unwrap(); writeln!(stdout, "< {}", message.trim_end()).unwrap(); } })); for thread in threads { thread.join().unwrap(); } }
关键说明
- 使用
Arc<Mutex<io::Stdout>>实现跨线程的互斥访问:Arc用于多线程共享所有权,Mutex保证同一时间只有一个线程能操作 stdout。 - Sender线程在打印提示符并刷新后,立即
drop(stdout)释放锁,避免在等待用户输入的长时间里占用锁,影响Receiver的输出。 - Receiver使用
writeln!直接操作锁保护的stdout,同时用trim_end()去掉输入字符串末尾的换行符,避免输出出现多余空行。
修改后两个线程的输出操作会被串行化,就能得到预期的输出顺序。
内容的提问来源于stack exchange,提问作者Shlomo Gottlieb
相关产品推荐
相关产品推荐

