C++. Сборник рецептов - [168]

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

уничтожается, >mutex_ разблокируется. Если >lock конструируется для объекта >mutex, который уже заблокирован другим потоком, текущий поток переходит в состояние ожидания до тех пор, пока >lock не окажется доступен.

Такой подход поначалу может показаться немного странным: а почему бы мьютексу >mutex не иметь методы >lock и >unlock? Применение класса >scoped_lock, который обеспечивает блокировку при конструировании и разблокировку при уничтожении, на самом деле более удобно и менее подвержено ошибкам. Когда вы создаете блокировку, используя >scoped_lock, мьютекс блокируется на весь период существования объекта >scoped_lock, т.е. вам не надо ничего разблокировать в явной форме на каждой ветви вычислений. С другой стороны, если вам приходится явно разблокировать захваченный мьютекс, необходимо гарантировать перехват любых исключений, которые могут быть выброшены в вашей функции (или где-нибудь выше ее в стеке вызовов), и гарантировать разблокировку >mutex. При использовании >scoped_lock, если выбрасывается исключение или функция возвращает управление, объект >scoped_lock автоматически уничтожается и >mutex разблокируется.

Использование мьютекса позволяет сделать всю работу, однако хочется немного большего. При таком подходе нет различия между чтением и записью, что существенно, так как неэффективно заставлять потоки ждать в очереди доступа к ресурсу, когда многие из них выполняют только операции чтения, для которых не требуется монопольный доступ. Для этого в библиотеке Boost Threads предусмотрен класс >read_write_mutex. Пример 12.3 показывает, как можно реализовать пример 12.2, используя >read_write_mutex с функцией-членом >front, которая позволяет вызывающей программе получить копию первого элемента очереди без его выталкивания.

Пример 12.3. Использование мьютекса чтения/записи

>#include

>#include

>#include

>#include


>template

>class Queue {

> public:

> Queue() : // Использовать мьютекс чтения/записи и придать ему приоритет

>           // записи

> rwMutex_(boost::read_write_scheduling_policy::writer_priority) {}

> ~Queue() {}

> void enqueue(const T& x) {

>  // Использовать блокировку чтения/записи, поскольку enqueue

>  // обновляет состояние

>  boost::read_write_mutex::scoped_write_lock writeLock(rwMutex_);

>  list_.push_back(x);

> }


> T dequeue() {

>  // Снова использовать блокировку для записи

>  boost::read_write_mutex::scoped_write_lock writeLock(rwMutex_);

>  if (list_.empty())

>   throw "empty!";

>  T tmp = list_.front();

>  list_.pop_front();

>  return(tmp);

> }


> T getFront() {

>  // Это операция чтения, поэтому требуется блокировка только для чтения

>  boost::read_write_mutex::scoped_read_lock.readLock(rwMutex_);

>  if (list_.empty())

>   throw "empty!";

>  return(list_.front());

> }

>private:

> std::list list_;

> boost::read_write_mutex rwMutex_;

>};


>Queue queueOfStrings;


>void sendSomething() {

> std::string s;

> for (int i = 0, i < 10; ++i) {

>  queueOfStrings.enqueue("Cyrus");

> }

>}


>void checkTheFront() {

> std::string s;

> for (int i=0; i < 10; ++i) {

>  try {

>   s = queueOfStrings.getFront();

>  } catch(...) {}

> }

>}


>int main() {

> boost::thread thr1(sendSomething);

> boost::thread_group grp;

> grp.сreate_thread(checkTheFront);

> grp.create_thread(checkTheFront);

> grp.сreate_thread(checkTheFront);

> grp_create_thread(checkTheFront);

> thr1.join();

> grp.join_all();

>}

Здесь необходимо отметить несколько моментов. Обратите внимание, что теперь я использую >read_write_mutex.

>boost::read_write_mutex rwMutex_;

При использовании мьютексов чтения/записи блокировки тоже выполняются иначе. В примере 12.3, когда мне нужно заблокировать >Queue для записи, я создаю объект класса >scoped_write_lock.

>boost::read_write_mutex::scoped_write_lock writeLock(rwMutex_);

А когда мне просто требуется прочитать >Queue, я использую >scoped_read_lock.

>boost::read_write_mutex::scoped_read_lock readLock(rwMutex_);

Блокировки чтения/записи удобны, но они не предохраняют вас от серьезных ошибок. На этапе компиляции не делается проверка ресурса, представленного мьютексом >rwMutex_, гарантирующая отсутствие изменения ресурса при блокировке только для чтения. Вы сами должны позаботиться о том, чтобы поток мог модифицировать состояние объекта только при блокировке для записи, поскольку компилятор это не будет делать.

Точная последовательность выполнения блокировок определяется политикой их планирования; эту политику вы задаете при конструировании объекта mutex. В библиотеке Boost Threads предусматривается четыре политики.

>reader_priority

Потоки, ожидающие выполнения блокировки для чтения, ее получат раньше потоков, ожидающих выполнения блокировки для записи.

>writer_priority

Потоки, ожидающие выполнения блокировки для записи, ее получат раньше потоков, ожидающих выполнения блокировки для чтения.

>alternating_single_read

Чередуются блокировки для чтения и для записи. Один читающий поток получает возможность блокировки для чтения, когда подходит «очередь» читающих потоков. Эта политика в целом отдает приоритет записывающим потокам. Например, если мьютекс заблокирован для записи и имеется несколько потоков, ожидающих блокировки для чтения, а также один поток, ожидающий блокировки для записи, сначала будет выполнена одна блокировка для чтения, затем блокировка для записи и после нее — все остальные блокировки для чтения. Подразумевается, что за это время не будет новых запросов на блокировку.


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Pro Git

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


Java 7

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


Фундаментальные алгоритмы и структуры данных в Delphi

Книга "Фундаментальные алгоритмы и структуры данных в Delphi" представляет собой уникальное учебное и справочное пособие по наиболее распространенным алгоритмам манипулирования данными, которые зарекомендовали себя как надежные и проверенные многими поколениями программистов. По данным журнала "Delphi Informant" за 2002 год, эта книга была признана сообществом разработчиков прикладных приложений на Delphi как «самая лучшая книга по практическому применению всех версий Delphi».В книге подробно рассматриваются базовые понятия алгоритмов и основополагающие структуры данных, алгоритмы сортировки, поиска, хеширования, синтаксического разбора, сжатия данных, а также многие другие темы, тесно связанные с прикладным программированием.


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

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


Как пасти котов. Наставление для программистов, руководящих другими программистами

«Как пасти котов» – это книга о лидерстве и руководстве, о том, как первое совмещать со вторым. Это, если хотите, словарь трудных случаев управления IT-проектами. Программист подобен кошке, которая гуляет сама по себе. Так уж исторически сложилось. Именно поэтому так непросто быть руководителем команды разработчиков. Даже если вы еще месяц назад были блестящим и дисциплинированным программистом и вдруг оказались в роли менеджера, вряд ли вы знаете, с чего надо начать, какой выбрать стиль руководства, как нанимать и увольнять сотрудников, проводить совещания, добиваться своевременного выполнения задач.