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

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

Потоки используют ресурсы, и в любом (хорошо спроектированном) многопоточном приложении управление доступом к таким ресурсам (к объектам, сокетам, файлам, «сырой» памяти и т.д.) осуществляется при помощи мьютексов, которые являются объектами, обеспечивающими последовательный доступ к каким-либо объектам со стороны нескольких потоков (см. рецепт 12.2). Если поток операционной системы оказывается «убитым», он не будет освобождать свои блокировки и свои ресурсы, подобно тому как «убитый» процесс не оставляет шансов на очистку буферов или правильное освобождение ресурсов операционной системы. Простое завершение потока в тот момент, когда вам кажется, что он должен быть завершен, — это все равно что убрать лестницу из-под маляра, когда время его работы закончилось.

Поэтому предусмотрена функция-член >join. Как показано в примере 12.1, вы можете вызвать >join, чтобы дождаться завершения работы дочернего потока, >join — это вежливый способ уведомления потока, что вы собираетесь ждать завершения его работы.

>myThread.join();

Поток, вызвавший функцию >join, переходит в состояние ожидания, пока не закончит свою работу другой поток, представленный объектом >myThread. Если он никогда не завершится, то никогда не завершится и >join. Применение >join — наилучший способ ожидания завершения работы дочернего потока.

Возможно, вы заметили, что, если передать что-либо осмысленное функции >threadFun, но закомментировать >join, поток не завершит свою работу. Вы можете убедиться в этом, выполняя в >threadFun цикл или какую-нибудь продолжительную операцию. Это объясняется тем, что операционная система уничтожает процесс вместе со всеми его дочерними процессами независимо от того, закончили или нет они свою работу. Без вызова >join функция >main не будет ждать окончания работы своих дочерних потоков: она завершается, и поток операционной системы уничтожается.

Если требуется создать несколько потоков, рассмотрите возможность их группирования в объект >thread_group. Объект >thread_group может управлять объектами двумя способами. Во-первых, вы можете вызвать >add_thread с указателем на объект >thread, и этот объект будет добавлен в группу. Ниже приводится пример.

>boost::thread_group grp;

>boost::thread* p = new boost::thread(threadFun);

>grp.add_thread(p);

>// выполнить какие-нибудь действия...

>grp.remove_thread(p);

При вызове деструктора >grp он удалит оператором >delete каждый указатель потока, который был добавлен в >add_thread. По этой причине вы можете добавлять в >thread_group только указатели объектов потоков, размещённых в динамической памяти. Удаляйте поток путем вызова >remove_thread с передачей адреса объекта потока (>remove_thread находит в группе соответствующий объект потока, сравнивая значения указателей, а не сами объекты). >remove_thread удалит указатель, ссылающийся на этот поток группы, но вам придется все же удалить сам поток с помощью оператора >delete.

Кроме того, вы можете добавить поток в группу, не создавая его непосредственно, а используя для этого вызов функции >create_thread, которая (подобно объекту потока) принимает функтор в качестве аргумента и начинает его выполнение в новом потоке операционной системы. Например, для порождения двух потоков и добавления их в группу сделайте следующее.

>boost::thread_group grp;

>grp.create_thread(threadFun);

>grp.create_thread(threadFun); // Теперь группа grp содержит два потока

>grp.join_all(); // Подождать завершения всех потоков

При добавлении потоков в группу при помощи >create_thread или >add_thread вы можете вызвать >join_all для ожидания завершения работы всех потоков группы. Вызов >join_all равносилен вызову >join для каждого потока группы: >join_all возвращает управление после завершения работы всех потоков группы.

Создание объекта потока позволяет начать выполнение отдельного потока. Однако с помощью средств библиотеки Boost Threads это делается обманчиво легко, поэтому необходимо тщательно обдумывать проект. Прочтите остальные рецепты настоящей главы, где даются дополнительные предостережения относительно применения потоков.

Смотри также

Рецепт 12.2.

12.2. Обеспечение потокозащищенности ресурсов

Проблема

В программе используется несколько потоков и требуется гарантировать невозможность модификации ресурса несколькими потоками одновременно. В целом это называется обеспечением потокозащищенности (thread-safe) ресурсов или сериализацией доступа к ним.

Решение

Используйте класс >mutex, определенный в boost/thread/mutex.hpp, для синхронизации доступа к потокам. Пример 12.2 показывает, как можно использовать в простых случаях объект >mutex для управления параллельным доступом к очереди.

Пример 12.2. Создание потокозащищенного класса

>#include

>#include

>#include


>// Простой класс очереди; в реальной программе вместо него следует

>// использовать std::queue

>template

>class Queue {

>public:

> Queue() {}

> ~Queue() {}

> void enqueue(const T& x) {

>  // Блокировать мьютекс для этой очереди

>  boost::mutex::scoped_lock lock(mutex_);

>  list_.push_back(x);

>  // scoped_lock автоматически уничтожается (и, следовательно, мьютекс


Рекомендуем почитать
Изучаем 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-проектами. Программист подобен кошке, которая гуляет сама по себе. Так уж исторически сложилось. Именно поэтому так непросто быть руководителем команды разработчиков. Даже если вы еще месяц назад были блестящим и дисциплинированным программистом и вдруг оказались в роли менеджера, вряд ли вы знаете, с чего надо начать, какой выбрать стиль руководства, как нанимать и увольнять сотрудников, проводить совещания, добиваться своевременного выполнения задач.