Критические секции
В очередь, сукины дети, в очередь!
М. А. Булгаков "Собачье сердце"
#pragma omp critical (name)
С помощью директивы critical мы можем указать участок кода, который будет исполняться только одним потоком в один момент времени. Если один из потоков начал выполнение критической секции с данным именем, то остальные потоки, начавшие выполнение этой же секции, будут заблокированы. Они будут ждать своей очереди. Как только первый поток завершит выполнение секции, один из заблокированных потоков войдёт в неё. Выбор следующего потока, который будет выполнять критическую секцию, будет случайным.
#include <omp.h>
void main()
{
int x;
x = 0;
#pragma omp parallel shared(x)
{
#pragma omp critical
x = x + 1;
}
}
Критические секции могут быть именованными или не именованными. В различных ситуациях улучшает производительность. Согласно стандарту все критические секции без имени, будут ассоциированы одним именем.
Присвоение имени позволит вам одновременно выполнять две и более критические секции одновременно.
#pragma omp critical
{
doworkA();
}
#pragma omp critical
{
doworkB();
}
// будет завершена секция с doworkA() , а только потом с doworkB()
#pragma omp critical (first)
{
doworkA();
}
#pragma omp critical (second)
{
doworkB();
}
//секции с doworkA () и doworkB() будут выполнены одновременно
Очень важно что бы имя критической секции не было названием системной функции или же было идентично уже использованному в проекте.
Есть случаи когда именовать нужно одним именем. Когда у вас в обоих критических секциях вывод в один и тот же файл или же вывод на экран.
#pragma omp critical
{
cout << "Привет от потока № " << omp_get_thread_num()<<endl;
}
//omp_get_thread_num() - библиотечная функция которая возвращает номер потока.
Обработка вложенных критических секций, может создать условие для наступления взаимной блокировки.
Критические секции удобный инструмент для разграничения доступа к общим данным. Однако, критическая секция привносит в параллельную программу последовательный код, что снижает эффективность. Следует применять только по необходимости. Ниже пример модификации кода с критической секцией.
//Алгоритм поиска максимального элемента массива
#pragma omp parallel for
for ( i = 0 ; i < N; ++i )
{
#pragma omp critical
{
if (arr[i] > max) max = arr[i];
}
}
//Мы можем вынести условие из критической секции
//т.к. условие будет положительным не каждой итерации
#pragma omp parallel for
for ( i = 0 ; i < N; ++i )
{
#pragma omp flush(max) if (arr[i] > max)
{
#pragma omp critical
{
if (arr[i] > max) max = arr[i];
}
}
}
Измененный код, будет более производительным.
Типичные ошибки:
Использование critical когда эффективнее использовать atomic
Большие объёмы работ внутри critical секции
Использование не нужной critical секции