Потоки стандартной библиотеки и синхронизация потоков
В стандартную библиотеку C++ включены стандартные, доступные на
всех реализациях C++, полностью поддерживающих стандарт, классы для
работы с потоками. Они определены в заголовке . Поток - это
последовательность инструкций, которая может выполняться одновременно с
другими потоками одновременно. Одновременность выполнения реализована
либо с помощью разделения выполнения потоков на отдельных физических
ядрах процессора (параллельное выполнение, parallelism) или с помощью
механизмов многозадачности как правило предоставляемых операционной
системой (многозадачность, concurrency).
Использование потоков на C++ происходит следующим образом:
#include
#include
using namespace std;
// обычная функция будет использоваться в качестве потока
int worker1(const string &str, int num)
{
cout << "function " << str << num << endl;
return num;
}
// функтор, или функция с состоянием, в качестве потока
class Worker
{
public:
int operator()(const string &str, int num) const
{
cout << "functor " << str << num << endl;
return num;
}
};
int main()
{
Worker worker2;
// лямбда в качестве потока
91
auto worker3 = [](const string &str, int num)
{
cout << "lambda " << str << num << endl;
return num;
};
// количество аппаратных исполниетелей потока
//
(как правило, количество ядер процессора)
cout << "hardware concurrency threads: "
<< thread::hardware_concurrency() << endl;
// создание и запуск потоков на исполнение
thread trd1(worker1, "worker thread No ", 1);
// вызов функции в отдельном
//
потоке (на отдельном ядре
//
процессора)
thread trd2(worker1, "worker thread No ", 2);
// ничто не мешает запустить
//
одно и то же дважды
thread trd3(worker2, "worker thread No ", 3);
// вызов функтора в отдельном
//
потоке
thread trd4(worker3, "worker thread No ", 4);
// вызов лямбда-выражение в
//
отдельном потоке
cout << "main thread" << endl;
// основной поток тоже выполняется
// ожидание завершения соответствующих потоков
trd1.join();
// ожидание завершения птока №1
trd2.join(); // ...
trd3.join();
trd4.join();
...
Можно заметить, что консольны вывод одновременно выполняющихся
потоков перемешивается. Чтобы этого избежать, а также упорядочить
совместный доступ к данным, необходимо использование методов
синхронизации потоков. В распоряжении программиста есть стандартное
средство - mutex (mutual exclusion, взаимное исключение). Взаимное
исключение защищает критическую секцию кода (critical section): захват
взаимного исключения заставляет другие потоки, пытающиеся захватить то же
взаимное исключение, ожидать его освобождения. Взаимные исключения
определены в заголовочном файле. В предыдущем примере использование
мьютексов позволит синхронихировать вывод потока в общую консоль. Для
этого перед выводом в консоль необходимо делать захват взаимного
исключения, и после завершения освобождать его, давая другим потокам
захватить мьютекс и начать свой вывод.
include
using namespace std;
mutex mtx;
92
int worker1(const string &str, int num)
{
mtx.lock();
cout << "function " << str << num << endl;
mtx.unlock();
return num;
}
В стандартной библиотеке определены и другие механизмы
синхронизации, которые следует подробно изучить, если потребуется
использовать многопоточное программирование. Также отлаженная и
устойчивая реализация многопоточности есть в нестандартных библиотеках:
Intel Threading Building Blocks (TBB), Boost.Threads, POCO.
Do'stlaringiz bilan baham: |