[英]How to terminate a blocking tokio task?
在我的应用程序中,我有一个阻塞任务,它同步地从队列中读取消息并将它们提供给正在运行的任务。 所有这些工作正常,但我遇到的问题是进程没有正确终止,因为queue_reader
任务没有停止。
我根据以下 tokio 文档构建了一个小示例: https://docs.rs/tokio/1.20.1/tokio/task/fn.spawn_blocking.html
use tokio::sync::mpsc;
use tokio::task;
#[tokio::main]
async fn main() {
let (incoming_tx, mut incoming_rx) = mpsc::channel(2);
// Some blocking task that never ends
let queue_reader = task::spawn_blocking(move || {
loop {
// Stand in for receiving messages from queue
incoming_tx.blocking_send(5).unwrap();
}
});
let mut acc = 0;
// Some complex condition that determines whether the job is done
while acc < 95 {
tokio::select! {
Some(v) = incoming_rx.recv() => {
acc += v;
}
}
}
assert_eq!(acc, 95);
println!("Finalizing thread");
queue_reader.abort(); // This doesn't seem to terminate the queue_reader task
queue_reader.await.unwrap(); // <-- The process hangs on this task.
println!("Done");
}
起初我希望queue_reader.abort()
应该终止任务,但事实并非如此。 我的期望是 tokio 只能对内部使用.await
的任务执行此操作,因为这将处理对tokio
的控制。 这是正确的吗?
为了终止queue_reader
任务,我引入了一个oneshot
通道,我通过它发出终止信号,如下一个片段所示。
use tokio::task;
use tokio::sync::{oneshot, mpsc};
#[tokio::main]
async fn main() {
let (incoming_tx, mut incoming_rx) = mpsc::channel(2);
// A new channel to communicate when the process must finish.
let (term_tx, mut term_rx) = oneshot::channel();
// Some blocking task that never ends
let queue_reader = task::spawn_blocking(move || {
// As long as termination is not signalled
while term_rx.try_recv().is_err() {
// Stand in for receiving messages from queue
incoming_tx.blocking_send(5).unwrap();
}
});
let mut acc = 0;
// Some complex condition that determines whether the job is done
while acc < 95 {
tokio::select! {
Some(v) = incoming_rx.recv() => {
acc += v;
}
}
}
assert_eq!(acc, 95);
// Signal termination
term_tx.send(()).unwrap();
println!("Finalizing thread");
queue_reader.await.unwrap();
println!("Done");
}
我的问题是,这是执行此操作的规范/最佳方法,还是有更好的选择?
Tokio 无法终止 CPU 绑定/阻塞任务。
杀死操作系统线程在技术上是可行的,但通常这不是一个好主意,因为创建新线程的成本很高,而且它可能会使您的程序处于无效的 state 中。 即使 Tokio 认为这是值得实现的东西,它也会严格限制它的实现 - 它会被强制进入多线程 model,只是为了支持您想要在完成之前终止阻塞任务的可能性。
您的解决方案非常好; 让您的阻塞任务自行终止,并提供一种方法来告诉它这样做。 如果这个未来是库的一部分,您可以通过将“句柄”返回给具有cancel()
方法的任务来抽象机制。
有更好的选择吗? 也许吧,但这取决于其他因素。 您的解决方案很好并且易于扩展,例如,如果您以后需要向任务发送不同类型的信号。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.