如何使用Tokio远程关闭正在运行的任务

15
我有一个UDP套接字在接收数据。
pub async fn start()  -> Result<(), std::io::Error> {
    loop {
        let mut data  = vec![0; 1024];
        socket.recv_from(&mut data).await?;
    }
}

当没有数据进来时,此代码当前被阻止在.await处。我想从我的主线程优雅地关闭服务器,那么我该如何向此.await发送信号,告诉它停止等待并关闭呢?


你可以使用 tokio::select 同时等待数据和关闭信号(你可以使用 tokio::broadcast)。 - user4815162342
1个回答

20

注意: Tokio网站有一篇关于优雅关闭的页面。

如果您需要终止多个任务,应使用广播通道发送关闭消息。您可以与tokio::select!一起使用。

use tokio::sync::broadcast::Receiver;

// You may want to log errors rather than return them in this function.
pub async fn start(kill: Receiver<()>) -> Result<(), std::io::Error> {
    tokio::select! {
        output = real_start() => output,
        _ = kill.recv() => Err(...),
    }
}

pub async fn real_start() -> Result<(), std::io::Error> {
    loop {
        let mut data  = vec![0; 1024];
        socket.recv_from(&mut data).await?;
    }
}

然后发送一条消息到该通道以终止所有任务。


为了只终止单个任务,您可以使用JoinHandle::abort方法,该方法会尽快终止任务。请注意,此方法仅适用于Tokio 1.x和0.3.x版本,并且要在Tokio 0.2.x中终止任务,请参见下面的下一节。
let task = tokio::spawn(start());

...

task.abort();

作为对JoinHandle::abort的替代方案,您可以使用来自futures crate的abortable。当您生成任务时,请执行以下操作:
let (task, handle) = abortable(start());
tokio::spawn(task);

然后稍后您可以通过调用abort方法来终止任务。

handle.abort();

当然,使用带有select!的通道也可以用于终止单个任务,可能与oneshot通道结合使用而不是广播通道。
所有这些方法都保证在.await处终止real_start方法。在两个.await之间运行代码时,不可能终止任务。您可以在此处阅读更多关于这个的信息。 mini-redis项目包含一个可访问的真实世界的服务器优雅关闭的示例。此外,Tokio教程有关于selectchannels的章节。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接