Namespaces
Variants

std::execution:: sequenced_policy, std::execution:: parallel_policy, std::execution:: parallel_unsequenced_policy, std::execution:: unsequenced_policy

From cppreference.net
Algorithm library
Constrained algorithms and algorithms on ranges (C++20)
Constrained algorithms, e.g. ranges::copy , ranges::sort , ...
Execution policies (C++17)
execution::sequenced_policy execution::parallel_policy execution::parallel_unsequenced_policy execution::parallel_unsequenced
(C++17) (C++17) (C++17) (C++20)
Non-modifying sequence operations
Batch operations
(C++17)
Search operations
Modifying sequence operations
Copy operations
(C++11)
(C++11)
Swap operations
Transformation operations
Generation operations
Removing operations
Order-changing operations
(until C++17) (C++11)
(C++20) (C++20)
Sampling operations
(C++17)

Sorting and related operations
Partitioning operations
Sorting operations
Binary search operations
(on partitioned ranges)
Set operations (on sorted ranges)
Merge operations (on sorted ranges)
Heap operations
Minimum/maximum operations
Lexicographical comparison operations
Permutation operations
C library
Numeric operations
Operations on uninitialized memory
Определено в заголовочном файле <execution>
class sequenced_policy { /* unspecified */ } ;
(1) (начиная с C++17)
class parallel_policy { /* unspecified */ } ;
(2) (начиная с C++17)
class parallel_unsequenced_policy { /* unspecified */ } ;
(3) (начиная с C++17)
class unsequenced_policy { /* unspecified */ } ;
(4) (начиная с C++20)
1) Тип политики выполнения, используемый как уникальный тип для разрешения неоднозначности перегрузки параллельных алгоритмов и требующий, чтобы выполнение параллельного алгоритма не могло быть распараллелено. Вызовы функций доступа к элементам в параллельных алгоритмах, вызванных с этой политикой (обычно указываемой как std::execution::seq ), являются недетерминированно последовательными в вызывающем потоке.
2) Тип политики выполнения, используемый как уникальный тип для разрешения перегрузки параллельных алгоритмов и указывающий, что выполнение параллельного алгоритма может быть распараллелено. Вызовы функций доступа к элементам в параллельных алгоритмах, вызванных с этой политикой (обычно указываемой как std::execution::par ), могут выполняться либо в вызывающем потоке, либо в потоке, неявно созданном библиотекой для поддержки выполнения параллельных алгоритмов. Любые такие вызовы, выполняющиеся в одном потоке, недетерминированно упорядочены друг относительно друга. Если потоки выполнения, созданные std::thread или std::jthread предоставляют гарантии конкурентного прогресса, то потоки выполнения, созданные библиотекой, предоставляют гарантии параллельного прогресса. В противном случае предоставляемая гарантия прогресса определяется реализацией. Примечание: параллельные гарантии прогресса означают, что если поток выполнения делает шаг, он в конечном счете сделает следующий шаг, что позволяет потокам входить в критические секции и захватывать блокировки, потому что поток, владеющий блокировкой, в конечном счете будет запланирован снова и сможет ее освободить.
3) Тип политики выполнения, используемый как уникальный тип для разрешения неоднозначности перегрузки параллельных алгоритмов и указывающий, что выполнение параллельного алгоритма может быть распараллелено, векторизовано или мигрировано между потоками (например, с помощью планировщика с крадущим родителем). Вызовы функций доступа к элементам в параллельных алгоритмах, вызванных с этой политикой, могут выполняться в неупорядоченном виде в неуказанных потоках и неупорядоченно относительно друг друга внутри каждого потока. Вызовы функций доступа к элементам в параллельных алгоритмах, вызванных с этой политикой, не могут вызывать операции, небезопасные для векторизации, такие как определенные стандартной библиотекой для синхронизации, включая операции с std::atomic и другими примитивами параллелизма. Если потоки выполнения, созданные с помощью std::thread или std::jthread предоставляют гарантии конкурентного прогресса, то потоки выполнения, созданные библиотекой, предоставляют гарантии слабо параллельного прогресса. В противном случае предоставляемая гарантия прогресса соответствует потоку, вызывающему параллельный алгоритм. Примечание: гарантии слабо параллельного прогресса обеспечивают, что один из потоков выполнения, который сделал шаг, в конечном счете сделает другой шаг, что не позволяет потокам входить в критические секции или захватывать блокировки, потому что поток, удерживающий блокировку, может не быть запланирован снова до тех пор, пока поток, пытающийся захватить блокировку, не завершится.
4) Тип политики выполнения, используемый как уникальный тип для разрешения неоднозначности перегрузки параллельных алгоритмов и указывающий, что выполнение параллельного алгоритма может быть векторизовано, например, выполнено в одном потоке с использованием инструкций, оперирующих несколькими элементами данных.

При выполнении параллельного алгоритма с любой из этих политик выполнения, если вызов функции доступа к элементу завершается неперехваченным исключением, std::terminate вызывается, но реализации могут определять дополнительные политики выполнения, которые обрабатывают исключения иначе.

Примечания

При использовании политики параллельного выполнения, ответственность за избежание состояний гонки данных и взаимных блокировок лежит на программисте:

int a[] = {0, 1};
std::vector<int> v;
std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int i)
{
    v.push_back(i * 2 + 1); // Ошибка: гонка данных
});
std::atomic<int> x {0};
int a[] = {1, 2};
std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int)
{
    x.fetch_add(1, std::memory_order_relaxed);
    while (x.load(std::memory_order_relaxed) == 1) { } // Ошибка: предполагается порядок выполнения
});
int x = 0;
std::mutex m;
int a[] = {1, 2};
std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int)
{
    std::lock_guard<std::mutex> guard(m);
    ++x; // корректно
});

Политики неупорядоченного выполнения — это единственный случай, когда вызовы функций являются неупорядоченными по отношению друг к другу, то есть они могут перемежаться. Во всех остальных ситуациях в C++ они являются неопределённо-упорядоченными (не могут перемежаться). Из-за этого пользователям запрещено выделять или освобождать память, захватывать мьютексы, использовать не lock-free std::atomic специализации или, в общем случае, выполнять любые небезопасные для векторизации операции при использовании этих политик (небезопасные для векторизации функции — это те, которые синхронизируются с другой функцией, например, std::mutex::unlock синхронизируется со следующим std::mutex::lock ).

int x = 0;
std::mutex m;
int a[] = {1, 2};
std::for_each(std::execution::par_unseq, std::begin(a), std::end(a), [&](int)
{
    std::lock_guard<std::mutex> guard(m); // Ошибка: конструктор lock_guard вызывает m.lock()
    ++x;
});

Если реализация не может распараллелить или векторизовать (например, из-за нехватки ресурсов), все стандартные политики выполнения могут переключиться на последовательное выполнение.

Смотрите также

(C++17) (C++17) (C++17) (C++20)
глобальные объекты политики выполнения
(константы)