std::cout << "Здравствуй, параллельный мир\n";
}
int
main() {
std::thread t(hello); ←
(3)
t.join(); ←
(4)
}
Прежде всего, отметим наличие дополнительной директивы #include
(1). Все объявления, необходимые для поддержки многопоточности, помещены в новые заголовочные файлы; функции и классы для управления потоками объявлены в файле
, а те, что нужны для защиты разделяемых данных, — в других заголовках.
Далее, код вывода сообщения перемещен в отдельную функцию (2). Это объясняется тем, что в каждом потоке должна быть main()
, а для всех остальных задается в конструкторе объекта std::thread
. В данном случае в качестве начальной функции объекта типа std::thread
, названного t
(3), выступает функция hello()
.
Есть и еще одно отличие вместо того, чтобы сразу писать на стандартный вывод или вызывать hello()
из main()
, эта программа запускает новый поток, так что теперь общее число потоков равно двум: главный, с начальной функцией main()
, и дополнительный, начинающий работу в функции hello()
.
После запуска нового потока (3) начальный поток продолжает работать. Если бы он не ждал завершения нового потока, то просто дошел бы до конца main()
, после чего исполнение программы закончилась бы быть может, еще до того, как у нового потока появился шанс начать работу. Чтобы предотвратить такое развитие событие, мы добавили обращение к функции join()
(4); в главе 2 объясняется, что это заставляет вызывающий поток (main()
) ждать завершения потока, ассоциированного с объектом std::thread
, — в данном случае t
.
Если вам показалось, что для элементарного вывода сообщения на стандартный вывод работы слишком много, то так оно и есть, — в разделе 1.2.3 выше мы говорили, что обычно для решения такой простой задачи не имеет смысла создавать несколько потоков, особенно если главному потоку в это время нечего делать. Но далее мы встретимся с примерами, когда запуск нескольких потоков дает очевидный выигрыш.
1.5. Резюме
В этой главе мы говорили о том, что такое параллелизм и многопоточность и почему стоит (или не стоит) использовать их в программах. Мы также рассмотрели историю многопоточности в С++ — от полного отсутствия поддержки в стандарте 1998 года через различные платформенно-зависимые расширения к полноценной поддержке в новом стандарте С++11. Эта поддержка, появившаяся очень вовремя, дает программистам возможность воспользоваться преимуществами аппаратного параллелизма, которые стали доступны в современных процессорах, поскольку их производители пошли но пути наращивания мощности за счет реализации нескольких ядер, а не увеличения быстродействия одного ядра.
Мы также видели (пример в разделе 1.4), как просто использовать классы и функции из стандартной библиотеки С++. В С++ использование нескольких потоков само по себе несложно — сложно спроектировать программу так, чтобы она вела себя, как задумано.
Закусив примерами из раздела 1.4, пора приступить к чему-нибудь более питательному. В главе 1 мы рассмотрим классы и функции для управления потоками.
Глава 2.
Управление потоками
■ Запуск потоков и различные способы задания кода, исполняемого в новом потоке.
■ Ждать завершения потока или позволить ему работать независимо?
■ Уникальные идентификаторы потоков.
Итак, вы решили написать параллельную программу, а конкретно — использовать несколько потоков. И что теперь? Как запустить потоки, как узнать, что поток завершился, и как отслеживать их выполнение? Средства, имеющиеся в стандартной библиотеке, позволяют относительно просто решить большинство задач управления потоками. Как мы увидим, почти все делается с помощью объекта std::thread
, ассоциированного с потоком. Для более сложных задач библиотека позволяет построить то, что нужно, из простейших кирпичиком.
Мы начнем эту главу с рассмотрения базовых операций: запуск потока, ожидание его завершения, исполнение в фоновом режиме. Затем мы поговорим о передаче дополнительных параметров функции потока в момент запуска и о том, как передать владение потока от одного объекта std::thread
другому. Наконец, мы обсудим вопрос о том, сколько запускать потоков и как идентифицировать отдельный поток.
2.1. Базовые операции управления потоками