编辑注释 - 此示例是在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
}
});
基于某些条件的一段时间后,我需要从程序的另一部分终止此线程。换句话说,我想退出无限循环。我该怎么做才能正确?另外,我怎么能暂停这个线程并在以后恢复呢?
我尝试使用全局不安全标志来打破循环,但我认为这个解决方案看起来不太好。
答案 0 :(得分:16)
对于终止和暂停线程,您都可以使用频道。
在工作循环的每次迭代中,我们检查是否有人通过渠道通知我们。如果是,或者如果频道的另一端超出范围,我们就会打破循环。
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
模块中找到其他并发原语。它们属于比通道更低的级别,但在特定任务中可以更有效。
答案 1 :(得分:0)
我本人已经多次回答这个问题,这就是我认为要解决OP的意图以及其他使线程自行停止的最佳实践。 Crossbeam以公认的答案为基础,是对mpsc的不错升级,它允许克隆和移动消息端点。它还具有方便的刻度功能。真正的重点是它具有不阻塞的try_recv()。
我不确定将消息检查器放在这样的操作循环中间会带来多大的帮助。我还没有发现Actix(或以前的Akka)真的可以停止线程而不需要(如上所述)让线程自己执行。因此,这就是我目前正在使用的(可以在此处进行更正,但仍然可以自我学习)。
// Cargo.toml:
// [dependencies]
// crossbeam-channel = "0.4.4"
use crossbeam_channel::{Sender, Receiver, unbounded, tick};
use std::time::{Duration, Instant};
fn main() {
let (tx, rx):(Sender<String>, Receiver<String>) = unbounded();
let rx2 = rx.clone();
// crossbeam allows clone and move of receiver
std::thread::spawn(move || {
// OP:
// let mut timer = Timer::new().unwrap();
// let periodic = timer.periodic(Duration::milliseconds(200));
let ticker: Receiver<Instant> = tick(std::time::Duration::from_millis(500));
loop {
// OP:
// periodic.recv();
crossbeam_channel::select! {
recv(ticker) -> _ => {
// OP: Do my work here
println!("Hello, work.");
// Comms Check: keep doing work?
// try_recv is non-blocking
// rx, the single consumer is clone-able in crossbeam
let try_result = rx2.try_recv();
match try_result {
Err(_e) => {},
Ok(msg) => {
match msg.as_str() {
"END_THE_WORLD" => {
println!("Ending the world.");
break;
},
_ => {},
}
},
_ => {}
}
}
}
}
});
// let work continue for 10 seconds then tell that thread to end.
std::thread::sleep(std::time::Duration::from_secs(10));
println!("Goodbye, world.");
tx.send("END_THE_WORLD".to_string());
}
将字符串用作消息设备对我来说有点难事。可以在枚举中执行另一个挂起并重新启动的操作。