MyTetra Share
Делитесь знаниями!
Что нового и удобного появилось в стандарте C++11 - Работа с могопоточностью
Время создания: 09.09.2019 02:16
Автор: xintrea
Текстовые метки: c++, thread, поток, многопоточность, параллельные вычисления
Раздел: Компьютер - Программирование - Язык C++ (Си++) - Стандарт C++11 и выше
Запись: xintrea/mytetra_syncro/master/base/1562607498rfvroq3qjv/text.html на raw.github.com

Работа с могопоточностью


В C++11 наконец-то появился нормальный интерфейс для работы с многопоточностью. Он реализован через класс thread библиотеки std. Подключается так:



#include <thread>



В отдельный поток обычно помещают просто какую-то функцию (или метод класса?). Создание потока выглядит так:



void myfunction()

{

...

}


int main(int arc, char** argv)

{

std::thread t( myfunction );

...

}



Данный код создаст поток, в котором сразу будет запущен на исполнение код функции myfunction(). Здесь видно, что в качестве параметра, конструктору потока t передается указатель на функцию myfunction(). А мы помним, что чтобы получить указатель на функцию, надо просто написать имя функции без скобок.



Код данной функции будет выполняться параллельно с кодом функции main(). Если выполнение функции main() завершится раньше чем код функции в потоке, поток будет остановлен на середине работы и удален. В консоль будет выведено предупреждение, примерно такое (для gcc++):



terminate called without an active exception

Aborted (core dumped)



Функция, которая создается для выполнения в потоке, должна возвращать void. Вроде как нет никакой возможности получить значение, возвращаемое такой функцией. На практике оно и не надо.


Класс std::thread предоставляет методы управления потоками. Они будут рассмотрены ниже.



Открепление потока


Наример, можно "открепить" созданный поток от основного потока программы:



int main(int arc, char** argv)

{

std::thread t( myfunction );

t.detach(); // Открепление

}



После такого "открепления", управлять данным потоком уже не получится. Он будет работать "сам по себе". Но останется возможность влиять на работу потока через переменные. Это будет возможно в случае, если функция, запущенная в потоке, вызывалась с параметрами (см. далее). Если функция main() завершится ранее функции в открепленном потоке, то, при завершении программы, открепленный поток будет просто молча завершен "на середине работы". Никаких ошибок сгенерировано не будет.



Ожидание завершения потока


Имеется возможность дождаться завершения работы потока. Это часто необходимо делать для организации синхронизации в многопоточной программе. Ожидание завершения работы потока делается с помощью метода join():



int main(int arc, char** argv)

{

std::thread t( myfunction );

...


t.join(); // Ожидание завершения потока


...

}



Слово join обозначает, что нужно "сомкнуть", "влить", "слить" поток t с текущим потоком. Если представить временной граф выполнения потоков, то это точка, где дополнительный поток сливается с основным. Естественно, в этой точке основной поток будет дожидаться завершения работы дополнительного потока.



Внимание! Поток, который был отправлен в свободное плавание (откреплен) с помощью метода detach(), невозможно засинхронизировать через метод join(), потому что контроль над таким потоком потерян.



Запуск потока с параметрами, передача данных между потоками


В функцию, которая запускается в потоке, можно передавать параметры. Параметры можно передавать по значению и по ссылке. Вот пример:



void myfunction(int a, int &b)

{

...

}


int main(int arc, char** argv)

{

int x=1, y=2;

std::thread t( myfunction, x, std::ref(y) );

...

}



Как видно из кода, параметры передаются следующими параметрами после указателя на функцию. Чтобы сформировать ссылку на переменную y, используется конструкция std::ref(y). Данная конструкция представляет собой одну из разновидностей "контейнера ссылки" (а еще бывает контейнер константной ссылки std::cref). В момент вызова функции произойдет преобразование типа, и функция будет вызвана, несмотря на то, что она принимает просто обычную ссылку на переменную.


Естественно, что изменение переменной b в функции myfunction() будет приводить к изменению переменной y в функции main(), ведь по сути это одна и та же область памяти.


Бонус! Хорошее обучающее видео: Многопоточность в C++11. Автор немного путается в терминологии, но основная идея по работе с потоками понятна.



Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования