了解tbb :: parallel_reduce的并发性

时间:2017-03-23 10:33:36

标签: c++ concurrency tbb

我使用tbb::parallel_reduce(range,body)作为documented here的命令形式。医生说:

  

parallel_reduce使用拆分构造函数为每个线程创建一个或多个正文副本。当body的operator()或方法join同时运行时,它可以复制一个body。您有责任确保此类并发的安全性。

因此,我了解Body::Body( Body&, split )可以Body::operator()Body::join( Body& rhs )运行。

我的问题是

1)Body::operator()Body::join( Body& rhs )可以同时运行吗?

2)Body::join( Body& rhs )可以安全地修改rhs吗?

W.r.t。 1),我相信只有在join完成后才能调用operator()。另外,看一下英特尔文档中的示例,很明显他们可以在没有数据竞争问题的情况下修改相同的数据:

struct Sum {
    float value;
    ...
    void operator()( const blocked_range<float*>& r ) {
        value = ...;
    }
    void join( Sum& rhs ) {value += rhs.value;}
};               

2 个答案:

答案 0 :(得分:2)

Body使用的tbb::parallel_reduce对象的生命周期为:

  1. 它是由拆分构造函数创建的另一个Body对象的“右侧”,后者又被更新为仅处理“左”侧。
  2. operator()适用于一个或多个后续范围。
  3. 它可以用作拆分新实体的构造函数的参数(是的,不止一次),并根据上面的#1进行更新。
  4. 在#3中为从它拆分的每个对象调用其方法join()
  5. 它作为rhs加入到它被拆分的正文对象中。
  6. 加入后会被销毁。
  7. 在上述操作中,#3可以同时运行到#2和#4;所有其余的都是顺序的,即使不一定由单个线程调用。换句话说,当一个体积累了一些子范围的部分缩减或者从另一个体连接结果时,可以通过拆分新体的构造函数来更新它。这可能是必要的保护,但通常这些调用访问身体的不同部分。

    所以,你的问题的答案是:

      

    1)Body::operator()Body::join( Body& rhs )可以同时运行吗?

    没有。仅在join()operator()的所有this来电之后调用rhs

      

    2)Body::join( Body& rhs )可以安全地修改rhs吗?

    是的,它可以。但除了可能在连接期间移动其内容并相应地更新状态之外,修改rhs没有多大意义,因为它会在join()调用之后立即销毁。

答案 1 :(得分:1)

有关并发调用方法所涉及的实例的文档不够清楚。但它提示:

  

在典型的使用中,安全性不需要额外的努力。

同一个实例没有并发性。因此,像在串行代码中一样拆分或连接两个实例是安全的。 operator()也不能在同一个实例上同时调用。所以文档必须说:

  

当body的operator()或方法join在不同的实例上同时运行时,它可以复制一个body。

BTW,引用

  

parallel_reduce递归地将范围拆分为该点的子范围,使得每个子范围的is_divisible()为false。

仅适用于simple_partitioner,对于其他分区,它可以在到达is_divisible() == false之前停止分割