Параллельное программирование на С++ в действии. Практика разработки многопоточных программ | страница 50
. Интерфейс предельно прост, он содержит только функции: >push()
и >pop()
.
Листинг 3.4. Определение класса потокобезопасного стека
>#include
>struct empty_stack: std::exception {
> const char* what() const throw();
>};
>template
>class threadsafe_stack {
>public:
> threadsafe_stack();
> threadsafe_stack(const threadsafe_stack&);
> threadsafe_stack& operator=(const threadsafe_stack&)
> = delete;←
(1)
> void push(T new_value);
> std::shared_ptr
> void pop(T& value);
> bool empty() const;
>};
Упростив интерфейс, мы добились максимальной безопасности — даже операции со стеком в целом ограничены: стек нельзя присваивать, так как оператор присваивания удален (1) (см. приложение А, раздел А.2) и функция >swap()
отсутствует. Однако стек можно копировать в предположении, что можно копировать его элементы. Обе функции >pop()
возбуждают исключение >empty_stack
, если стек пуст, поэтому программа будет работать, даже если стек был модифицирован после вызова >empty()
. В описании варианта 3 выше отмечалось, что использование >std::shared_ptr
позволяет стеку взять на себя распределение памяти и избежать лишних обращений к >new
и >delete
. Теперь из пяти операций со стеком осталось только три: >push()
, >pop()
и >empty()
. И даже >empty()
лишняя. Чем проще интерфейс, тем удобнее контролировать доступ к данным — можно захватывать мьютекс на все время выполнения операции. В листинге 3.5 приведена простая реализация в виде обертки вокруг класс >std::stack<>
.
Листинг 3.5. Определение класса потокобезопасного стека
>#include
>#include
>#include
>#include
>struct empty_stack: std::exception {
> const char* what() const throw();
>};
>template
>class threadsafe_stack {
>private:
> std::stack
> mutable std::mutex m;
>public:
> threadsafe_stack(){}
> threadsafe_stack(const threadsafe_stack& other) {
> std::lock_guard
> data = other.data; ←┐
(1) Копирование производится в теле
> } │
конструктора
> threadsafe_stack& operator=(const threadsafe_stack&) = delete;
> void push(T new_value) {
> std::lock_guard
> data.push(new_value);
> }
> std::shared_ptr
Перед тем как выталкивать значение,
> { ←┘
проверяем, не пуст ли стек
> std::lock_guard
> if (data.empty()) throw empty_stack();
> std::shared_ptr
> data.pop(); ←┐
Перед тем как модифицировать стек