yandex-writeups/3_1/3practice.md
2025-11-17 07:18:54 +07:00

3.8 KiB
Raw Blame History

Практическое задание: задачи с таймаутом

Создайте метод 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 залочен, что приводит к ожиданию анлока от других