编者注 - 此示例是在Rust 1.0之前创建的,并且从那时起特定类型已更改或已被删除。一般问题和概念仍然有效。
我已经产生了一个带有无限循环和计时器的线程。
thread::spawn(|| {
let mut timer = Timer::new().unwrap();
let periodic = timer.periodic(Duration::milliseconds(200));
loop {
periodic.recv();
// Do my work here
}
});
在基于某些条件的时间之后,我需要从程序的另一部分终止该线程。换句话说,我想退出无限循环。我该怎么做才能正确?另外,我怎么能暂停这个线程并在以后恢复呢?
我试图使用全局不安全标志来打破循环,但我认为这个解决方案看起来不太好。
对于终止和挂起线程,您可以使用通道。
在工作循环的每次迭代中,我们检查是否有人通过渠道通知我们。如果是,或者如果通道的另一端超出范围,我们就会打破循环。
use std::io::{self, BufRead};
use std::sync::mpsc::{self, TryRecvError};
use std::thread;
use std::time::Duration;
fn main() {
println!("Press enter to terminate the child thread");
let (tx, rx) = mpsc::channel();
thread::spawn(move || loop {
println!("Working...");
thread::sleep(Duration::from_millis(500));
match rx.try_recv() {
Ok(_) | Err(TryRecvError::Disconnected) => {
println!("Terminating.");
break;
}
Err(TryRecvError::Empty) => {}
}
});
let mut line = String::new();
let stdin = io::stdin();
let _ = stdin.lock().read_line(&mut line);
let _ = tx.send(());
}
我们使用recv()
暂停线程,直到某些东西到达通道。为了恢复该线程,您需要通过该频道发送内容;在这种情况下单位价值()
。如果通道的发送端被丢弃,recv()
将返回Err(())
- 我们使用它来退出循环。
use std::io::{self, BufRead};
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
println!("Press enter to wake up the child thread");
let (tx, rx) = mpsc::channel();
thread::spawn(move || loop {
println!("Suspending...");
match rx.recv() {
Ok(_) => {
println!("Working...");
thread::sleep(Duration::from_millis(500));
}
Err(_) => {
println!("Terminating.");
break;
}
}
});
let mut line = String::new();
let stdin = io::stdin();
for _ in 0..4 {
let _ = stdin.lock().read_line(&mut line);
let _ = tx.send(());
}
}
频道是执行这些任务的最简单,最自然(IMO)的方式,但不是最有效的方法。您可以在std::sync
模块中找到其他并发原语。它们属于比通道更低的级别,但在特定任务中可以更有效。
理想的解决方案是Condvar
。您可以在wait_timeout
中使用std::sync module
,如pointed out by @Vladimir Matveev。
这是文档中的示例:
use std::sync::{Arc, Mutex, Condvar}; use std::thread; use std::time::Duration; let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair2 = pair.clone(); thread::spawn(move|| { let &(ref lock, ref cvar) = &*pair2; let mut started = lock.lock().unwrap(); *started = true; // We notify the condvar that the value has changed. cvar.notify_one(); }); // wait for the thread to start up let &(ref lock, ref cvar) = &*pair; let mut started = lock.lock().unwrap(); // as long as the value inside the `Mutex` is false, we wait loop { let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap(); // 10 milliseconds have passed, or maybe the value changed! started = result.0; if *started == true { // We received the notification and the value has been updated, we can leave. break } }