std::execution::sequenced_policy, std::execution::parallel_policy, std::execution::parallel_unsequenced_policy, std::execution::unsequenced_policy
Определён в заголовочном файле <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) |
std::execution::seq), неопределённо упорядочены в вызывающем потоке.std::execution::par), могут выполняться как в вызывающем потоке, так и в потоке, неявно созданном библиотекой для поддержки выполнения параллельного алгоритма. Такие вызовы, выполняемые в одном и том же потоке, неопределённо упорядочены относительно друг друга. Если потоки выполнения, созданные std::thread или std::jthread, предоставляют гарантии конкурентного прогресса, то потоки выполнения, созданные библиотекой, предоставляют гарантии параллельного прогресса. В противном случае предоставляемая гарантия прогресса определяется реализацией. Примечание: гарантии параллельного прогресса означают, что если поток выполнения делает шаг, то в конечном итоге он сделает и следующий шаг. Это позволяет потокам входить в критические секции и захватывать блокировки, поскольку поток, удерживающий блокировку, в конечном итоге снова будет запланирован и сможет её освободить.std::atomic и другие примитивы параллелизма. Если потоки выполнения, созданные std::thread или std::jthread, предоставляют гарантии конкурентного прогресса, то потоки выполнения, созданные библиотекой, предоставляют гарантии слабого параллельного прогресса. В противном случае предоставляется гарантия прогресса, соответствующая потоку, вызывающему параллельный алгоритм. Примечание: гарантии слабого параллельного прогресса означают, что один из потоков выполнения, сделавших шаг, в конечном итоге сделает и следующий шаг. Это не позволяет потокам входить в критические секции и захватывать блокировки, поскольку поток, удерживающий блокировку, может не быть запланирован снова до тех пор, пока поток, пытающийся захватить блокировку, не завершит выполнение.Во время выполнения параллельного алгоритма с любой из этих политик выполнения, если вызов функции доступа к элементам завершается из-за необработанного исключения, вызывается 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); // Error: data race
});
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) { } // Error: assumes execution order
});
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; // correct
});
Политики неупорядоченного выполнения — единственный случай, когда вызовы функций не упорядочены относительно друг друга, то есть могут чередоваться. Во всех остальных ситуациях в C++ они неопределённо упорядочены (не могут чередоваться). Поэтому при использовании этих политик пользователям запрещено выделять или освобождать память, захватывать мьютексы, использовать специализации 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); // Error: lock_guard constructor calls m.lock()
++x;
});
Если реализация не может распараллелить или векторизовать выполнение (например, из-за нехватки ресурсов), все стандартные политики выполнения могут перейти к последовательному выполнению.
См. также
|
(C++17)(C++17)(C++17)(C++20) | глобальные объекты политик выполнения (константы) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/algorithm/execution_policy_tag_t