📌 Условие:
Реализуйте потокобезопасную структуру данных — очередь с приоритетами (`PriorityQueue`), которая:
- Позволяет добавлять элементы с приоритетом через push(value: T, priority: u32)
.
- Позволяет забирать элемент с наивысшим приоритетом через pop() -> Option<T>
.
- Гарантирует:
- Безопасную работу из нескольких потоков без блокировок на долгие периоды (желательно через минимальные локи или атомарные операции).
- Отдачу элементов в порядке убывания приоритета.
- Высокую производительность при массовых операциях.
Ограничения:
- Можно использовать стандартные библиотеки Rust (`std::sync`, `std::collections`).
- Нельзя использовать внешние библиотеки вроде tokio
, crossbeam
, rayon
и др.
- Структура должна быть универсальной (`Generic`), т.е. работать с любыми типами данных.
---
▪️ Подсказки:
- Для внутреннего хранения можно использовать BinaryHeap
из std::collections
.
- Для обеспечения многопоточности можно использовать Arc<Mutex<...>>
.
- Чтобы минимизировать блокировки, можно подумать о:
- Разделении кучи на несколько шардов (`sharding`),
- Использовании тонкой блокировки только на операции изменения состояния.
---
▪️ Что оценивается:
- Умение правильно работать с владением (`ownership`) и заимствованием (`borrowing`) в многопоточной среде.
- Аккуратное управление блокировками (`Mutex`, `RwLock`).
- Оптимизация под высокую нагрузку (минимизация времени блокировки).
- Чистота и читабельность кода.
- Способность правильно обрабатывать ошибки (`PoisonError` при падении потока).
---
▪️ Разбор возможного решения:
Идея архитектуры:
- Основная структура — Arc<Mutex<BinaryHeap<...>>>
.
- Каждый push
и pop
блокирует мьютекс на короткое время (захватывает лок на минимальное изменение).
- При push(value, priority)
:
- Оборачиваем значение в структуру, которая реализует Ord
так, чтобы приоритет был главным критерием сортировки.
- При pop()
:
- Просто pop()
из BinaryHeap
.
- Обработка ошибок:
- При отравлении мьютекса (`PoisonError`) безопасно восстанавливать структуру или пробрасывать ошибку выше.
---
▪️ Мини-пример структуры:
use std::collections::BinaryHeap;
use std::sync::{Arc, Mutex};
#[derive(Eq, PartialEq)]
struct Item<T> {
priority: u32,
value: T,
}
impl<T> Ord for Item<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
other.priority.cmp(&self.priority) // обратный порядок для max-heap
}
}
impl<T> PartialOrd for Item<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
pub struct PriorityQueue<T> {
heap: Arc<Mutex<BinaryHeap<Item<T>>>>,
}
impl<T> PriorityQueue<T> {
pub fn new() -> Self {
PriorityQueue {
heap: Arc::new(Mutex::new(BinaryHeap::new())),
}
}
pub fn push(&self, value: T, priority: u32) {
let mut heap = self.heap.lock().unwrap();
heap.push(Item { priority, value });
}
pub fn pop(&self) -> Option<T> {
let mut heap = self.heap.lock().unwrap();
heap.pop().map(|item| item.value)
}
}
---
▪️ Возможные подводные камни:
- ❗ Не обработать
PoisonError
— если поток паникует при блокировке, вся очередь будет "сломана".- ❗ Долгая блокировка внутри
push
или pop
, особенно при больших объемах данных.- ❗ Возможная гонка состояний, если попытаться вручную обойти
Mutex
через небезопасный код (`unsafe`).---
▪️ Дополнительные вопросы на собеседовании:
- Как модифицировать структуру для поддержки тайм-аутов на
pop()
(если очередь пуста — ждать максимум N миллисекунд)?- Как бы вы реализовали "разделение очереди на несколько шардов" для снижения конкуренции потоков?
- Как сделать неблокирующую версию через
Atomic
примитивы?---
@rust_code