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

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

(3)

>std::thread t3;     ←(4)

>t3 = std::move(t2); ←(5)

>t1 = std::move(t3); ←(6) Это присваивание приводит

>;                       к аварийному завершению программы

Сначала создастся новый поток (1) и связывается с объектом >t1. Затем владение явно передается объекту >t2 в момент его конструирования путем вызова >std::move()(2). В этот момент с >t1 уже не связан никакой поток выполнения: поток, в котором исполняется функция >some_function, теперь связан с >t2.

Далее создается еще один поток, который связывается с временным объектом типа >std::thread(3). Для последующей передачи владения объекту >t1 уже не требуется явный вызов >std::move(), так как владельцем является временный объект, а передача владения от временных объектов производится автоматически и неявно.

Объект >t3 конструируется по умолчанию (4), а это означает, что в момент создания с ним не связывается никакой поток. Владение потоком, который в данный момент связан с >t2, передастся объекту >t3(5), опять-таки путем явного обращения к >std::move(), поскольку >t2 — именованный объект. После всех этих перемещений >t1 оказывается связан с потоком, исполняющим функцию >some_other_function, >t2 не связан ни с каким потоком, a >t3 связан с потоком, исполняющим функцию >some_function .

Последнее перемещение (6) передает владение потоком, исполняющим >some_function, обратно объекту >t1, в котором исполнение этой функции началось. Однако теперь с >t1 уже связан поток (который исполнял функцию >some_other_function), поэтому вызывается >std::terminate(), и программа завершается. Так делается ради совместимости с поведением деструктора >std::thread. В разделе 2.1.1 мы видели, что нужно либо явно ждать завершения потока, либо отсоединить его до момента уничтожения; то же самое относится и к присваиванию: нельзя просто «прихлопнуть» поток, присвоив новое значение объекту >std::thread, который им управляет.

Поддержка операции перемещения в классе >std::thread означает, что владение можно легко передать при возврате из функции, как показано в листинге 2.5.


Листинг 2.5. Возврат объекта >std::thread из функции

>std::thread f() {

> void some_function();

> return std::thread(some_function);

>}


>std::thread g() {

> void some_other_function(int);

> std::thread t(some_other_function, 42);

> return t;

>}

Аналогично, если требуется передать владение внутрь функции, то достаточно, чтобы она принимала экземпляр >std::thread по значению в качестве одного из параметров, например:

>void f(std::thread t);


>void g() {

> void some_function();

> f(std::thread(some_function));

> std::thread t(some_function);

> f(std::move(t));

>}

Одно из преимуществ, которые даёт поддержка перемещения в классе >std::thread, заключается в том, что мы можем модифицировать класс >thread_guard из листинга 2.3, так чтобы он принимал владение потоком. Это позволит избежать неприятностей в случае, когда время жизни объекта >thread_guard оказывает больше, чем время жизни потока, на который он ссылается, а, кроме того, это означает, что никто другой не сможет присоединиться к потоку или отсоединить его, так как владение было передано объекту >thread_guard. Поскольку основное назначение этого класса гарантировать завершение потока до выхода из области видимости, я назвал его >scoped_thread. Реализация и простой пример использования приведены в листинге 2.6.


Листинг 2.6. Класс >scoped_thread и пример его использования

>class scoped_thread {

> std::thread t;

>public:

> explicit scoped_thread(std::thread t_) : ←(1)

> t(std::move(t_)) {

> if (!t.joinable()) ←(2)

>  throw std::logic_error("No thread");

> }

> ~scoped_thread() {

>  t.join();         ←(3)

> }

> scoped_thread(scoped_thread const&)=delete;

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

>};


>struct func; ←см. листинг 2.1


>void f() {

> int some_local_state;

> scoped_thread t(std::thread(func(some_local_state))); ←(4)

> do_something_in_current_thread();

>}                   ←(5)

Этот пример очень похож на приведенный в листинге 2.3, только новый поток теперь передается непосредственно конструктору >scoped_thread(4), вместо того чтобы создавать для него отдельную именованную переменную. Когда новый поток достигает конца >f(5), объект >scoped_thread уничтожается, а затем поток соединяется (3) с потоком, переданным конструктору (1). Если в классе >thread_guard из листинга 2.3 деструктор должен был проверить, верно ли, что поток все еще допускает соединение, то теперь мы можем сделать это в конструкторе (2) и возбудить исключение, если это не так.

Поддержка перемещения в классе >std::thread позволяет также хранить объекты этого класса в контейнере при условии, что класс контейнера поддерживает перемещение (как, например, модифицированный класс >std::vector<>). Это означает, что можно написать код, показанный в листинге 2.7, который запускает несколько потоков, а потом ждет их завершения.


Листинг 2.7. Запуск нескольких потоков и ожидание их завершения

>void do_work(unsigned id);


>void f() {

> std::vector threads;

> for (unsigned i = 0; i < 20; ++i) {           │Запуск

>  threads.push_back(std::thread(do_work(i))); ←┘потоков

> }                                            │Поочередный


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

Это знаменитый бестселлер, который научит вас использовать власть массового сотрудничества и покажет, как применять викиномику в вашем бизнесе. Переведенная более чем на двадцать языков и неоднократно номинированная на звание лучшей бизнес-книги, "Викиномика" стала обязательным чтением для деловых людей во всем мире. Она разъясняет, как массовое сотрудничество происходит не только на сайтах 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 так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.