Как удаленно завершить выполнение задач с помощью Tokio

#rust #rust-tokio

#Ржавчина #rust-tokio

Вопрос:

У меня есть сокет UDP, который получает данные

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

В настоящее время этот код заблокирован .await , когда данные не поступают. Я хочу корректно завершить работу моего сервера из моего основного потока, так как мне отправить ему сигнал о том .await , что он должен перестать спать и вместо этого отключиться?

Комментарии:

1. Вы можете использовать tokio::select для ожидания данных и сигнала завершения работы (для которого вы можете использовать tokio:: broadcast ) одновременно.

Ответ №1:

Примечание: На веб-сайте 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(amp;mut data).await?;
    }
}
  

Затем, чтобы завершить все задачи, отправьте сообщение по каналу.


Чтобы убить только одну задачу, вы можете использовать JoinHandle::abort метод, который убьет задачу как можно скорее. Обратите внимание, что этот метод доступен только в Tokio 1.x и 0.3.x, а чтобы прервать задачу с помощью Tokio 0.2.x, см. Следующий раздел ниже.

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

...

task.abort();
  

В качестве альтернативы JoinHandle::abort вы можете использовать abortable из ящика futures. Когда вы создаете задачу, вы делаете следующее:

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

Затем позже вы можете завершить выполнение задачи, вызвав abort метод.

 handle.abort();
  

Конечно, канал с select! также может использоваться для завершения одной задачи, возможно, в сочетании с oneshot каналом, а не с широковещательным каналом.


Все эти методы гарантируют, что real_start метод будет уничтожен при .await . Невозможно завершить выполнение задачи, пока она выполняет код между двумя .await секундами. Вы можете прочитать больше о том, почему это здесь.

Проект mini-redis содержит доступный реальный пример плавного завершения работы сервера. Кроме того, в руководстве Tokio есть главы, посвященные как выбору, так и каналам.