繁体   English   中英

如何终止阻塞的 tokio 任务?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM