std::execution::sequenced_policy、std::execution::parallel_policy、std::execution::parallel_unsequenced_policy、std::execution::unsequenced_policy
来自 cppreference.com
在头文件 <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); // 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) |
全局执行策略对象 (常量) |