Параллельное программирование на С++ в действии. Практика разработки многопоточных программ | страница 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 pop();

> 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 data;

> mutable std::mutex m;

>public:

> threadsafe_stack(){}

> threadsafe_stack(const threadsafe_stack& other) {

>  std::lock_guard lock(other.m);

>  data = other.data; ←┐(1) Копирование производится в теле

> }                    │конструктора

> threadsafe_stack& operator=(const threadsafe_stack&) = delete;


> void push(T new_value) {

>  std::lock_guard lock(m);

>  data.push(new_value);

> }


> std::shared_ptr pop()│Перед тем как выталкивать значение,

> {                      ←┘проверяем, не пуст ли стек

>  std::lock_guard lock(m);

>  if (data.empty()) throw empty_stack();

>  std::shared_ptr const res(std::make_shared(data.top()));

>  data.pop(); ←┐Перед тем как модифицировать стек