Параллельное программирование на С++ в действии. Практика разработки многопоточных программ - [25]

Шрифт
Интервал

Это решение, пусть и безопасное, не идеально. Хотя на этапе компиляции можно узнать, существует ли копирующий или перемещающий конструктор, который не возбуждает исключений, — с помощью концепций >std::is_nothrow_copy_constructible, >std::is_nothrow_move_constructible и характеристик типов, но это слишком ограничительное требование. Пользовательских типов, в которых копирующий конструктор может возбуждать исключение и перемещающего конструктора нет, гораздо больше, чем типов, в которых копирующий и (или) перемещающий конструктор гарантированно не возбуждают исключений (хотя ситуация может измениться, когда разработчики привыкнут к появившейся в С++11 поддержке ссылок на r-значения). Было бы крайне нежелательно запрещать хранение таких объектов в потокобезопасном стеке.

Вариант 3: возвращать указатель на вытолкнутый элемент

Третий вариант — возвращать не копию вытолкнутого элемента по значению, а указатель на него. Его достоинство в том, указатели можно копировать, не опасаясь исключений, поэтому указанную Каргиллом проблему мы обходим. А недостаток в том, что возврат указателя заставляет искать средства для управления выделенной объекту памятью, так что для таких простых типов, как целые числа, накладные расходы на управление памятью могут превысить затраты на возврат типа по значению. В любом интерфейсе, где применяется этот вариант, в качестве типа указателя было бы разумно избрать >std::shared_ptr; мало того что это предотвращает утечки памяти, поскольку объект уничтожается вместе с уничтожением последнего указателя на него, так еще и библиотека полностью контролирует схему распределения памяти и не требует использования >new и >delete. Это существенно с точки зрения оптимизации — требование, чтобы память для всякого хранящегося в стеке объекта выделялась с помощью >new, повлекло бы заметные накладные расходы по сравнению с исходной версией, небезопасной относительно потоков.

Вариант 4: реализовать одновременно вариант 1 и один из вариантов 2 или 3

Никогда не следует пренебрегать гибкостью, особенно в обобщенном коде. Если остановиться на варианте 2 или 3, то будет сравнительно нетрудно реализовать и вариант 1, а это оставит пользователю возможность выбрать наиболее подходящее решение ценой очень небольших накладных расходов.

Пример определения потокобезопасного стека

В листинге 3.4 приведено определение класса стека со свободным от гонок интерфейсом. В нем реализованы приведенные выше варианты 1 и 3: имеется два перегруженных варианта функции-члена >pop() — один принимает ссылку на переменную, в которой следует сохранить значение, а второй возвращает >std::shared_ptr<>. Интерфейс предельно прост, он содержит только функции: >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(); ←┐Перед тем как модифицировать стек


Еще от автора Энтони Д Уильямс
Викиномика. Как массовое сотрудничество изменяет всё

Это знаменитый бестселлер, который научит вас использовать власть массового сотрудничества и покажет, как применять викиномику в вашем бизнесе. Переведенная более чем на двадцать языков и неоднократно номинированная на звание лучшей бизнес-книги, "Викиномика" стала обязательным чтением для деловых людей во всем мире. Она разъясняет, как массовое сотрудничество происходит не только на сайтах Wikipedia и YouTube, но и в традиционных компаниях, использующих технологии для того, чтобы вдохнуть новую жизнь в свои предприятия.Дон Тапскотт и Энтони Уильямс раскрывают принципы викиномики и рассказывают потрясающие истории о том, как массы людей (как за деньги, так и добровольно) создают новости, изучают геном человека, создают ремиксы любимой музыки, находят лекарства от болезней, редактируют школьные учебники, изобретают новую косметику, пишут программное обеспечение и даже строят мотоциклы.Знания, ресурсы и вычислительные способности миллиардов людей самоорганизуются и превращаются в новую значительную коллективную силу, действующую согласованно и управляемую с помощью блогов, вики, чатов, сетей равноправных партнеров и личные трансляции.


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Симуляция частичной специализации

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка событий в С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.