像for_each这样的并行算法与周围的代码同步吗? [英] Do parallel algorithms like for_each synchronize with surrounding code?

查看:17
本文介绍了像for_each这样的并行算法与周围的代码同步吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是在思考Thread sanitizer warnings after using parallel std::for_each时出现的。

具有并行执行策略的std::for_each算法可以在实现创建的工作线程中执行代码。这些线程是否与调用线程对for_each的调用和返回同步,或者类似的情况?常识似乎表明它们应该这样做,但我在C++20标准中找不到保证。

考虑以下简单示例(try on godbolt):

#include <algorithm>
#include <execution>
#include <iostream>

void increment(int &a) {
    a++;
}

int main(void) {
    constexpr size_t n = 1000;
    static int arr[n];
    arr[0] = 3;
    std::for_each(std::execution::par, arr, arr+n, increment);
    std::cout << arr[0] << std::endl;
    return 0;
}

这将始终输出4

该实现可以在另一个线程中调用increment(arr[0]),该线程执行arr[0]++。主线程中的存储arr[0] = 3是否发生在arr[0]++之前?同样,arr[0]++是否发生在std::cout << arr[0]arr[0]的加载之前?我天真地认为他们应该这样做,但我看不到任何方法来证明这一点。算法。并行似乎不包含与周围代码同步的任何内容。

如果不是,则该示例包含数据竞争,并且其行为未定义。这将使std::execution::par的正确使用变得相当困难,我想知道这是否是缺陷。


如果没有这样的保证,可以想象该实现可能会执行以下操作:

std::atomic<int *> work = nullptr;

void do_work() {
    int *p;
    while (!(p = work.load(std::memory_order_relaxed)))
        std::this_thread::yield();
    (*p)++;
}

// started at program startup
std::thread worker_thread(do_work);

int main() {
    // ...
    arr[0] = 3;
    // for_each does the following:
    work.store(&arr[0], std::memory_order_relaxed);
    worker_thread.join();
    // ...
}

如果是这样的话,我们真的会有一场数据竞赛。

推荐答案

使用cppreference

用作唯一类型的执行策略类型,用于消除并行算法重载的歧义,并指示并行算法的执行可以并行化。允许使用此策略(通常指定为std::Execution::PAR)调用并行算法中的元素访问函数,以在调用线程或由库隐式创建的线程中执行,以支持并行算法执行。在同一线程中执行的任何此类调用彼此之间的顺序不确定。

std::for_each中(逻辑上)创建的线程中完成的操作是在线程创建后排序的。

发件人the draft

允许使用Execution​::​PARALLEL_POLICY类型的执行策略对象调用并行算法中的元素访问函数,以在调用执行的线程或由库隐式创建的执行线程中执行,以支持并行算法执行。 如果由线程([thread.thread.class])或j线程([thread.jthread.class])创建的执行线程提供并发向前进度保证([Intro.Progress]),则由库隐式创建的执行线程将提供并行向前进度保证;否则,所提供的向前进度保证是实现定义的。 在同一执行线程中执行的任何此类调用彼此之间的顺序不确定。

措辞略有不同,但相似。

我想您可以绕过它;没有明确的保证由库隐式创建的支持并行算法执行的线程需要在foreach方法中创建(或联接)。

但需要满足各种算法的后置条件,它应该处理";After";问题;如何保证后置条件在std::for_each返回之前发生,但可以保证后置条件已经发生。在我看来,应用程序发生在std::for_each返回之前。

对于启动排序,我能做的最好的事情就是阅读标准,这意味着它必须在std::for_each中为此目的而创建的线程行为,因此有排序保证。但我承认这个措辞有点含糊,由图书馆创作的是相当被动的语气。

这篇关于像for_each这样的并行算法与周围的代码同步吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆