3.8 KiB
Практическое задание: задачи с таймаутом
Создайте метод TaskPool::create, которая будет отправлять задачу в канал, но если не получается отправить задачу в канал в течение 100 миллисекунд, возвращается None
use std::time::{Duration, Instant};
use tokio::{
sync::mpsc::{Receiver, Sender, channel, error::SendError},
time::timeout,
};
struct TaskPool<T> {
tasks: Receiver<T>,
sender: Sender<T>,
}
impl<T> TaskPool<T> {
fn new(queue_size: usize) -> Self {
let (sender, tasks) = channel::<T>(queue_size);
Self { tasks, sender }
}
async fn create(&self, task: T) -> Option<Result<(), SendError<T>>> {
// Напишите свое решение здесь
}
async fn pull_task(&mut self) -> Option<T> {
self.tasks.recv().await
}
}
#[tokio::main]
async fn main() {
let mut tasks = TaskPool::new(2);
assert_eq!(tasks.create(()).await, Some(Ok(())));
assert_eq!(tasks.create(()).await, Some(Ok(())));
let start = Instant::now();
assert_eq!(tasks.create(()).await, None);
let end = start.elapsed();
assert!(end > Duration::from_millis(90));
assert!(end < Duration::from_millis(110));
assert_eq!(tasks.pull_task().await, Some(()));
assert_eq!(tasks.create(()).await, Some(Ok(())));
}
Подсказки
используйте tokio::time::timeout для таймаута tokio::time::timeout принимает фьючерс для которого нужен таймаут используйте .send() для отправки значение обратите внимание, что .send() возвращает фьючерс
Проверьте свой код по чек-листу: используется tokio::time::timeout используются асинхронные каналы все тесты проходят
Решение
async fn create(&self, task: T) -> Option<Result<(), SendError<T>>> {
let sender = self.sender.send(task);
timeout(Duration::from_millis(100), sender).await.ok()
}
Вы знаете про существование таких синхронизационных примитивов в стандартной библиотеке, как Mutex, RwLock, каналы. И в тоже происходит ожидание. Но они блокируют поток, поэтому в tokio есть альтернативы с идентичным интерфейсом, но асинхронным:
У асинхронных мьютекса и RwLock есть преимущество: если фьючерс ожидает лока асинхронного мьютекса, токио будет обратно переключаться на другие фьючерсы. В то время, как синхронные версии будут держать поток и не давать другим фьючерсам исполняться. Для синхронных версий это создает проблему: если MutexGuard пересек границу .await, где, к примеру, ожидается ответ в течение 30 секунд, то другой фьючерс, ожидающий лока мьютекса, будет держать целый поток токио в течение всего этого времени, что приведет к потере производительности. А если рантайм токио будет однопоточным, то программа зависнет навсегда. после вызова await залочен, что приводит к ожиданию анлока от других