首页 > 解决方案 > 避免竞争条件性能

问题描述

我有两个异步线程(明确要求这样做)从一个对象运行 2 个不同的方法,move_robot()并且get_area(). 第一种方法使车辆移动通过不同的位置,第二种方法计算到目前为止所覆盖的区域。两者共享两个对象属性:

vector<double> position; // saves current position of the vehicle
bool moving; // true if the vehicle has not reach the end of the path

为了避免竞争条件,我使用以下方式锁定:

   void get_area() {
        std::unique_lock<std::mutex> lck(mtx); // get mutex
        static vector<double> previous_measure = this->position; // initialized with the first position the robot is in
        lck.unlock();
        while (this->moving) {
            lck.lock();
            auto auxiliar = this->position;
            lck.unlock();
            this->area_covered += distance(auxiliar , previous_measure) * this->width;
            previous_measure = this->position; // save the new position for the next iteration
            this_thread::sleep_for(chrono::milliseconds(10)); // sleep for 10 ms
            std::cout << "Area:" << this->area_covered << " m²" << std::endl;
        }
    }

    void next_step() {
        std::unique_lock<std::mutex> lck(mtx); // get mutex
        this->position[0] += DT * this->direction[0];
        this->position[1] += DT * this->direction[1];

    }

    void move_robot(const vector<vector<double> > &map) {
    // accumulate the footprint while the robot moves
    // Iterate through the path
        for (unsigned int i=1; i < map.size(); i++) {
            while (distance(position , map[i]) > DISTANCE_TOLERANCE ) {
                this->direction = unitary_vector(map[i], this->position);
                this->next_step();
                this_thread::sleep_for(chrono::milliseconds(10)); // sleep for 10 ms
            }
        }
        this->moving = false; // notify get area to leave
    }

get_area()方法中,我锁定以将this->position属性保存在变量中,并在整个迭代中具有相同的属性并初始化第一个previous_measure. 在根据车辆的动态变化next_step()时使用。this->position

此外,您将如何将moving属性锁定在 while 条件中?以及如何避免这种情况:我执行get_area(),机器人移动并结束路径,我离开get_area方法而不进行最后一次迭代。

我的问题是是否可以优化这种锁定和解锁,因为它经常进行(我不想设置生产者-客户端结构,因为我希望 get_area() 独立运行)。


编辑:

我已经将条件更改为:

bool is_moving() {
    std::unique_lock<std::mutex> lck(mtx);
    return moving;
}



void get_area() {
    std::unique_lock<std::mutex> lck(mtx);
    static vector<double> previous_measure = this->position; 
    lck.unlock();
    while (this->is_moving()) {...}
}

但不知道有没有更干净的东西

标签: c++multithreading

解决方案


优化#1:假设机器人的速度比计算速度慢

考虑到机器人的速度,并没有真正需要锁定两者positionmoving因为这个值的变化率与计算的速度相比会非常小,所以stale读取的误差范围可以忽略不计。

在任何情况下,上面的代码都是这样做的,因为它previous_measure = this->position正在读取它的解锁时间,因此在循环结束position时它的可能性很小。previous_measure != auxiliar

为了确保执行最终读取(在为假get_area后再次计算含义moving),我们可以在离开循环后再次重复计算,因为那时机器人已经停止移动并且位置永远不会改变。

优化#2:消费者-生产者通过 Ring Buffer 进行通信

如果变量的变化速度接近计算速度,优化#1显然会产生很大的误差。在这种情况下,您可以考虑使用环形缓冲区将值从 移动move_robotget_area不锁定。如果你只有一个消费者,只有一个生产者线程,则不需要加锁。是一个实现环形缓冲区(或循环缓冲区)的示例。


推荐阅读