c++ - 避免竞争条件性能
问题描述
我有两个异步线程(明确要求这样做)从一个对象运行 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()) {...}
}
但不知道有没有更干净的东西
解决方案
优化#1:假设机器人的速度比计算速度慢
考虑到机器人的速度,并没有真正需要锁定两者position
,moving
因为这个值的变化率与计算的速度相比会非常小,所以stale
读取的误差范围可以忽略不计。
在任何情况下,上面的代码都是这样做的,因为它previous_measure = this->position
正在读取它的解锁时间,因此在循环结束position
时它的可能性很小。previous_measure != auxiliar
为了确保执行最终读取(在为假get_area
后再次计算含义moving
),我们可以在离开循环后再次重复计算,因为那时机器人已经停止移动并且位置永远不会改变。
优化#2:消费者-生产者通过 Ring Buffer 进行通信
如果变量的变化速度接近计算速度,优化#1显然会产生很大的误差。在这种情况下,您可以考虑使用环形缓冲区将值从 移动move_robot
到get_area
不锁定。如果你只有一个消费者,只有一个生产者线程,则不需要加锁。这是一个实现环形缓冲区(或循环缓冲区)的示例。
推荐阅读
- swift - 在 Swift 中否定 @available
- python - 使用 qtbot.mouseClick 选择 QListWidgetItem
- c# - 实例化 Prefab 返回 NullReferenceException 错误
- c# - 从 FCM 生成令牌时出现以下错误
- html - DOM 不是首先搜索具有类深度的元素吗?
- javascript - 如何在 React 中使用 i18n-iso-countries?
- javascript - 滚动。修复滚动部分的时间:最后很慢
- vue.js - 如何使用 vuex(非父/子组件)在不同的组件上运行函数
- java - 如何将两个二维数组“粘贴”在一起?
- visual-studio - Visual Studio:项目名称中的程序集名称和默认命名空间