72 lines
3.8 KiB
Markdown
72 lines
3.8 KiB
Markdown
### **Практическое задание: задачи с таймаутом**
|
||
|
||
Создайте метод TaskPool::create, которая будет отправлять задачу в канал, но если не получается отправить задачу в канал в течение 100 миллисекунд, возвращается None
|
||
```rust
|
||
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
|
||
используются асинхронные каналы
|
||
все тесты проходят
|
||
### **Решение**
|
||
```rust
|
||
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 залочен, что приводит к ожиданию анлока от других |