How to remotely shut down running tasks with Tokio

回眸只為那壹抹淺笑 提交于 2021-01-03 08:29:46

问题


I have a UDP socket that is receiving data

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

This code is currently blocked on the .await when there is no data coming in. I want to gracefully shut down my server from my main thread, so how do I send a signal to this .await that it should stop sleeping and shut down instead?


回答1:


Note: This answer currently links to the 1.x version of Tokio, but applies to Tokio 0.2 and 0.3 as well.

If you have more than one task to kill, you should use a broadcast channel to send shutdown messages. You can use it together with 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?;
    }
}

Then to kill all the tasks, send a message on the channel.


To kill only a single task, you can use the JoinHandle::abort method, which will kill the task as soon as possible. Note that this method is available only in Tokio 1.x and 0.3.x, and to abort a task using Tokio 0.2.x, see the next section below.

let task = tokio::spawn(start());

...

task.abort();

As an alternative to JoinHandle::abort, you can use abortable from the futures crate. When you spawn the task, you do the following:

let (task, handle) = abortable(start());
tokio::spawn(task);

Then later you can kill the task by calling the abort method.

handle.abort();

Of course, a channel with select! can also be used to kill a single task, perhaps combined with an oneshot channel rather than a broadcast channel.


All of these methods guarantee that the real_start method is killed at an .await. It is not possible to kill the task while it is running code between two .awaits. You can read more about why this is here.

The mini-redis project contains an accessible real-world example of graceful shutdown of a server. Additionally, the Tokio tutorial has chapters on both select and channels.



来源:https://stackoverflow.com/questions/64084955/how-to-remotely-shut-down-running-tasks-with-tokio

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!