#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 есть главы, посвященные как выбору, так и каналам.