如何在 C++ 中迭代点云的循环中使用多线程?

How to use multi-threading within a loop that iterates through a point cloud in C++?

我制作了一个函数来估计 3D 点云的法向量,在 200 万大小的点云上 运行 需要花费大量时间。我想通过同时在两个不同点上调用相同的函数来实现多线程,但它不起作用(它创建了数百个线程)。这是我尝试过的:

// kd-tree used for finding neighbours
pcl::KdTreeFLANN<pcl::PointXYZRGB> kdt;

// cloud iterators
pcl::PointCloud<pcl::PointXYZRGB>::iterator cloud_it = pt_cl->points.begin();
pcl::PointCloud<pcl::PointXYZRGB>::iterator cloud_it1;
pcl::PointCloud<pcl::PointXYZRGB>::iterator cloud_it2;
pcl::PointCloud<pcl::PointXYZRGB>::iterator cloud_it3;
pcl::PointCloud<pcl::PointXYZRGB>::iterator cloud_it4;

// initializing tree
kdt.setInputCloud(pt_cl);

// loop exit condition
bool it_completed = false;

while (!it_completed)
{
    // initializing cloud iterators
    cloud_it1 = cloud_it;
    cloud_it2 = cloud_it++;
    cloud_it3 = cloud_it++;

    if (cloud_it3 != pt_cl->points.end())
    {
        // attaching threads
        boost::thread thread_1 = boost::thread(geom::vectors::find_normal, pt_cl, cloud_it1, kdt, radius, max_neighbs);
        boost::thread thread_2 = boost::thread(geom::vectors::find_normal, pt_cl, cloud_it2, kdt, radius, max_neighbs);
        boost::thread thread_3 = boost::thread(geom::vectors::find_normal, pt_cl, cloud_it3, kdt, radius, max_neighbs);

        // joining threads
        thread_1.join();
        thread_2.join();
        thread_3.join();

        cloud_it++;
    }

    else
    {
        it_completed = true;
    }
}

如您所见,我试图同时在 3 个不同的点上调用同一个函数。关于如何使这项工作有什么建议吗?对不起,我的代码很糟糕,我很累,提前谢谢你。

编辑:这里是 find_normal 函数 以下是参数:

@param pt_cl is a pointer to the point cloud to be treated (pcl::PointCloud<PointXYZRGB>::Ptr)
@param cloud_it is an iterator of this cloud (pcl::PointCloud<PointXYZRGB>::iterator)
@param kdt is the kd_tree used to find the closest neighbours of a point
@param radius defines the range in which to search for the neighbours of a point
@param max_neighbs is the maximum number of neighbours to be returned by the radius search

// auxilliary vectors for the k-tree nearest search
    std::vector<int> pointIdxRadiusSearch; // neighbours ids
    std::vector<float> pointRadiusSquaredDistance; // distances from the source to the neighbours

    // the vectors of which the cross product calculates the normal
    geom::vectors::vector3 *vect1;
    geom::vectors::vector3 *vect2;
    geom::vectors::vector3 *cross_prod;
    geom::vectors::vector3 *abs_cross_prod;
    geom::vectors::vector3 *normal;
    geom::vectors::vector3 *normalized_normal;

    // vectors to average
    std::vector<geom::vectors::vector3> vct_toavg;

    // if there are neighbours left
    if (kdt.radiusSearch(*cloud_it, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance, max_neighbs) > 0)
    {

        for (int pt_index = 0; pt_index < (pointIdxRadiusSearch.size() - 1); pt_index++)
        {
            // defining the first vector
            vect1 = geom::vectors::create_vect2p((*cloud_it), pt_cl->points[pointIdxRadiusSearch[pt_index + 1]]);

            // defining the second vector; making sure there is no 'out of bounds' error
            if (pt_index == pointIdxRadiusSearch.size() - 2)
                vect2 = geom::vectors::create_vect2p((*cloud_it), pt_cl->points[pointIdxRadiusSearch[1]]);


            else
                vect2 = geom::vectors::create_vect2p((*cloud_it), pt_cl->points[pointIdxRadiusSearch[pt_index + 2]]);

            // adding the cross product of the two previous vectors to our list
            cross_prod = geom::vectors::cross_product(*vect1, *vect2);
            abs_cross_prod = geom::aux::abs_vector(*cross_prod);
            vct_toavg.push_back(*abs_cross_prod);

            // freeing memory
            delete vect1;
            delete vect2;
            delete cross_prod;
            delete abs_cross_prod;
        }

        // calculating the normal
        normal = geom::vectors::vect_avg(vct_toavg);

        // calculating the normalized normal
        normalized_normal = geom::vectors::normalize_normal(*normal);

        // coloring the point
        geom::aux::norm_toPtRGB(&(*cloud_it), *normalized_normal);

        // freeing memory
        delete normal;
        delete normalized_normal;

        // clearing vectors
        vct_toavg.clear();
        pointIdxRadiusSearch.clear();
        pointRadiusSquaredDistance.clear();

        // shrinking vectors
        vct_toavg.shrink_to_fit();
        pointIdxRadiusSearch.shrink_to_fit();
        pointRadiusSquaredDistance.shrink_to_fit();
    }

由于我不太了解结果数据的存储方式,我将建议一个基于 OpenMP 的解决方案,该解决方案与您发布的代码相匹配。

// kd-tree used for finding neighbours
pcl::KdTreeFLANN<pcl::PointXYZRGB> kdt;

#pragma openmp parallel for schedule(static)
for (pcl::PointCloud<pcl::PointXYZRGB>::iterator cloud_it = pt_cl->points.begin();
    cloud_it < pt_cl.end();
    ++cloud_it) {
    geom::vectors::find_normal, pt_cl, cloud_it, kdt, radius, max_neighbs);
}

请注意,您应该使用 < 比较,而不是 != 比较,这就是 OpenMP 的工作方式(它需要随机访问迭代器)。我正在使用 static 时间表,因为每个元素都应该花费或多或少相同的时间来处理。如果不是这种情况,请尝试使用 schedule(dynamic)

此解决方案使用 OpenMP,您可以进行调查,例如TBB 也是如此,尽管它比 OpenMP 具有更高的进入门槛并且使用 OOP 风格 API.

此外,重复我在评论中已经说过的话:OpenMP 和 TBB 将为您处理线程管理和负载分配。您只需向他们传递有关如何操作的提示(例如 schedule(static)),以便更好地满足您的需求。

除此之外,请养成尽可能少重复代码的习惯;理想情况下,不应重复任何代码。例如。当你声明了很多相同类型的变量,或者用类似的模式连续调用某个函数几次等等。我也看到代码中有过多的注释,背后的原因不明。