std::unique_lock(2)
std::unique_lock
data_cond.wait(
head_lock, [&]{return head.get() != get_tail();});
return std::move(head_lock); ←(3)
}
std::unique_ptr
std::unique_lock(4)
return pop_head();
}
std::unique_ptr
std::unique_lock(5)
value = std::move(*head->data);
return pop_head();
}
public:
std::shared_ptr
std::unique_ptr
return old_head->data;
}
void wait_and_pop(T& value) {
std::unique_ptr
}
};
В реализации извлечения из очереди используется несколько небольших вспомогательных функций, которые упрощают код и позволяют устранить дублирование, например: pop_head() (1) (модификация списка в результате удаления головного элемента) и wait_for_data() (2) (ожидание появления данных в очереди). Особенно стоит отметить функцию wait_for_data(), потому что она не только ждет условную переменную, используя лямбда-функцию в качестве предиката, но и возвращает объект блокировки вызывающей программе (3). Тем самым мы гарантируем, что та же самая блокировка удерживается, пока данные модифицируются в соответствующем перегруженном варианте wait_pop_head() (4), (5). Функция pop_head() используется также в функции try_pop(), показанной ниже.
Листинг 6.10. Потокобозопасная очередь с блокировкой и ожиданием: try_pop() и empty()
template
class threadsafe_queue {
private:
std::unique_ptr
std::lock_guard
if (head.get() == get_tail()) {
return std::unique_ptr
}
return pop_head();
}
std::unique_ptr
std::lock_guard
if (head.get() == get_tail()) {
return std::unique_ptr
}
value = std::move(*head->data);
return pop_head();
}
public:
std::shared_ptr
std::unique_ptr
return old_head ? old_head->data : std::shared_ptr
}
bool try_pop(T& value) {
std::unique_ptr
return old_head;
}
void empty() {
std::lock_guard
return (head.get() == get_tail());
}
};
Эта реализация очереди ляжет в основу очереди без блокировок, которую мы будем рассматривать в главе 7. Данная очередь
Показанную реализацию неограниченной очереди легко преобразовать в очередь с ограниченной длиной, введя ожидание условной переменной в функцию push(). Вместо того чтобы ждать, пока в очереди появятся элементы (как pop()), мы должны будем ждать, когда число элементов в ней окажется меньше максимума. Дальнейшее обсуждение ограниченных очередей выходит за рамки этой книги, мы же перейдём от очередей к более сложным структурам данных.
6.3. Проектирование более сложных структур данных с блокировками