Reputation: 1111
I have been asked to run a task with two asynchronous threads. One that is supposed to move a vehicle and the other calculates and plots the area covered by the vehicle. A simplification of the code is the following:
#include <iostream>
#include <vector>
#include <math.h>
#include <chrono>
using namespace std;
class Robot {
private:
bool moving;
vector<double> position;
double area_covered;
public:
Robot(const vector<double> &initial_position) {
moving = true;
position = initial_position;
area_covered = 0.0;
}
void get_area() {
static vector<double> previous_measure = this->position; // initialized with the first position the robot is in
while (this->moving) {
this->area_covered += sqrt(pow(this->position[0] - previous_measure[0]) + pow(this->position[1] - previous_measure[1]));
previous_measure = this->position; // save the new position for the next iteration
this_thread::sleep_for(chrono::milliseconds(600)); // sleep for 600 ms
}
}
void move_robot(const vector<vector<double> > &map) {
for (int i=1; i < map.size(); i++) {
this->position = map[i];
this_thread::sleep_for(chrono::milliseconds(500)); // sleep for 500 ms
}
this->moving = false;
}
};
int main () {
vector<vector<double> > path{
{0.0359, -0.013}, {0.0658, -0.0287}, {0.0736, -0.027}};
Robot r3(path[0]);
auto thread1 = std::async(&Robot::move_robot, &r3, path);
auto thread2 = std::async(&Robot::get_area, &r3);
thread1.join();
thread2.join();
return 0;
}
In method get_area()
I am using multiple times this.position
which may differ because it is changed in the other thread. I cannot block the other thread while doing get_area
but I must avoid using different this.position
in one loop run. The easiest solution is to create another variable to save the initial value of this.position
but I would like to know from you whether there is a better C++ way to do it. It would be something like:
void get_area() {
static vector<double> previous_measure = this->position; // initialized with the first position the robot is in
vector<double> auxiliar;
while (this->moving) {
auxiliar = this->position;
this->area_covered += sqrt(pow(auxiliar[0] - previous_measure[0]) + pow(auxiliar[1] - previous_measure[1]));
previous_measure = auxiliar; // save the new position for the next iteration
this_thread::sleep_for(chrono::milliseconds(600)); // sleep for 600 ms
}
}
Moreover, I need to notify get_area()
when the method / thread of move_robot()
finishes so that in the next while iteration it quits as well. Right now I am using the attribute moving
but doing so I am checking the condition before each iteration and not at the end. I could add an if
at the end to check it but there should be some better approaches.
Last, I would appreciate as well your opinion on how to cleanly pass the object to the two asynchronous threads and wait for them to solve the C++ way.
Upvotes: 0
Views: 159
Reputation: 19113
This is code is very unsafe and filled with UB and syntax errors.
timespan
variable does not put the thread to sleep.get_area
does not get called at all while move_robot
is iterating? Unlikely but it is also very unlikely to get 1:1 mapping.this.
is always wrong syntax.std::async
does not return a thread but std::future
instead.Do some research on threading in C++ - cppreference can also serve as a good tutorial with its examples.
You should rewrite the code with std::atomic<T>
for primitive types like this->moving
, and locking for others. Of course only if they are accessed from multiple threads and are not naturally thread-safe.
Because you require 1:1 mapping, use 1 producer, 1 consumer queue with std::condition_variable
:
move_robot
will push to the queue and notify the consumer.get_area
will wait on the notification, process enqueued elements and go back to sleep.get_area
that the work is done.Upvotes: 2