Запускаем и приостанавливаем потоки
Еще одним дополнением, появившимся в C++11, является класс std::thread
В данном примере мы реализуем программу, которая запускает и останавливает потоки. Далее рассмотрим информацию о том, что с ними делать после запуска.
Как это делается
В этом примере мы запустим несколько потоков и увидим, как ведет себя программа, когда мы задействуем несколько ядер процессора, чтобы выполнить разные части ее кода одновременно.
1. Сначала включим всего два заголовочных файла, а затем объявим об использовании пространств имен std
chrono_literals
:#include
#include
using namespace std;
using namespace chrono_literals;
2. Чтобы запустить поток, следует указать, какой код он должен выполнить. Поэтому определим функцию, которую ему нужно выполнить. Функции — естественные потенциальные входные точки для потоков. Наша функция-пример принимает аргумент i
cout
одновременно. Если такая ситуация произойдет, то выходные данные будут искажены. Другой пример в настоящей главе посвящен именно этой проблеме.static void thread_with_param(int i)
{
this_thread::sleep_for(1ms * i);
cout << "Hello from thread " << i << '\n';
this_thread::sleep_for(1s * i);
cout << "Bye from thread " << i << '\n';
}
3. В функции main
std::thread::hardware_concurrency
. Данное значение зависит от того, сколько ядер имеет процессор и сколько ядер поддерживается реализацией STL. Это говорит о том, что значения будут различаться для разных компьютеров.int main()
{
cout << thread::hardware_concurrency()
<< " concurrent threads are supported.\n";
4. Наконец, начнем работать с потоками. Запустим три потока с разными идентификаторами. При создании экземпляра потока с помощью выражения наподобие t{f, x}
f(x)
. Таким образом можно передавать функциям thread_with_param
разные аргументы для каждого потока: thread t1 {thread_with_param, 1};
thread t2 {thread_with_param, 2};
thread t3 {thread_with_param, 3};
5. Поскольку данные потоки запущены свободно, нужно остановить их, когда они закончат выполнять свою работу. Сделаем это с помощью функции join
t1.join();
t2.join();
6. Альтернативой присоединению является
join
или detach
, то все приложение завершится довольно шумно, как только будет выполнен деструктор объекта потока. Путем вызова функции detach
указываем thread
, что хотим продолжения работы потока номер 3 даже после того, как его экземпляр будет разрушен: t3.detach();
7. Перед завершением функции main
cout << "Threads joined.\n";
}
8. Компиляция и запуск программы дадут следующий результат. Мы можем увидеть, что моя машина имеет восемь ядер процессора. Далее сообщения
$ ./threads
8 concurrent threads are supported.
Hello from thread 1
Hello from thread 2
Hello from thread 3
Bye from thread 1
Bye from thread 2
Threads joined.
Как это работает
Запуск и остановку потоков выполнить очень просто. Многопроцессорная обработка начинает усложняться в момент, когда потокам нужно работать вместе (делить ресурсы, ожидать завершения других потоков и т.д.).