我正在学习C ++。
我需要等待接收一些数据才能启动主线程。
这是我要接收所需数据的回调函数:
/**
* Robot's initial position (X component).
*/
double init_pos_x = 0.0;
/**
* Robot's initial position (Y component).
*/
double init_pos_y = 0.0;
/**
* Used to get only the robot's initial position.
*/
bool is_init_pos = true;
/**
* Current robot's X position.
*/
double x = 0.0;
/**
* Current robot's Y position.
*/
double y = 0.0;
/**
* Current robot's orientation.
*/
tfScalar theta = 0.0;
/**
* Command velocity topic's name.
*/
const std::string cmdTopic = "/pioneer2dx/mybot/cmd_vel";
/**
* Odometry topic's name.
*/
const std::string odomTopic = "/pioneer2dx/mybot/odom_diffdrive";
/**
* Callback function executes when new topic data comes.
* Task of the callback function is to print data to screen.
*/
void odomCallback(const nav_msgs::Odometry::ConstPtr& msg)
{
// Update robot position (x, y).
x = msg->pose.pose.position.x;
y = msg->pose.pose.position.y;
// If this is the first position given by the odometry topic, then it is the
// initial position of the robot.
if (is_init_pos)
{
is_init_pos = false;
// Set robot's initial position.
init_pos_x = x;
init_pos_y = y;
}
这是主线程:
int main(int argc, char **argv)
{
ros::init(argc, argv, "astar_controller");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* Topic where we are going to publish speed commands.
*/
ros::Publisher cmd_vel_pub = n.advertise<geometry_msgs::Twist>(cmdTopic, 1000);
/**
* Topic where we are going to receive odometry messages.
*/
ros::Subscriber odom_sub = n.subscribe(odomTopic, 1000, odomCallback);
ros::Rate loop_rate(10);
//*********************************************
// I want to wait here.
//*********************************************
// More code...
}
我曾经考虑过使用while循环来检查is_init_pos
是否已更改,然后继续,但是我需要在while
循环内做一些事情(类似于thread.sleep())。
我如何等待在odomCallback
中收到第一个“消息”?
答案 0 :(得分:2)
全局地,声明在回调线程和main之间共享的条件变量和互斥量。还有一个处于互斥锁之下的布尔值。
#include <mutex>
#include <condition_variable>
std::condition_variable g_cv;
std::mutex g_mutex;
bool g_isReady = false;
在您的odomCallback返回之前,请执行以下操作以向信号main
发出信号,以表明信号可以继续
g_mutex.lock();
g_isReady = true;
g_mutex.unlock();
g_cv.notify_all();
然后,您等待如下:
//*********************************************
// I want to wait here.
//*********************************************
// Wait for odomCallback to signal its done
{
std::unique_lock<std::mutex> lock(g_mutex);
while (g_isReady == false)
{
g_cv.wait(lock);
}
}
答案 1 :(得分:0)
编辑:我认为您不应该使用多个线程。您可以通过在主线程中运行"event loop" 来等待回调。
//*********************************************
// I want to wait here.
//*********************************************
ros::spin();
请参见http://wiki.ros.org/roscpp/Overview/Callbacks%20and%20Spinning
原始答案如下:
首先,is_init_pos
变量需要安全才能从多个线程并行访问。否则,如果主线程读取它,而回调线程写入它,则您的程序将具有未定义的行为。因此,请使用互斥锁来同步对布尔值的访问,并使用条件变量在状态更改时发送通知:
bool is_init_pos = true;
std::mutex init_mutex;
std::condition_variable init_cv;
在访问is_init_pos
时,回调函数应锁定该互斥锁,并在条件变量上发送通知:
std::lock_guard<std::mutex> lock(init_mutex);
if (is_init_pos)
{
is_init_pos = false;
// Set robot's initial position.
init_pos_x = x;
init_pos_y = y;
init_cv.notify_one();
}
现在,当状态更改时,主线程可以等待条件变量来接收通知:
ros::Rate loop_rate(10);
//*********************************************
// I want to wait here.
//*********************************************
// New nested scope that determines the duration of the mutex lock
{
std::unique_lock<std::mutex> lock(init_mutex);
auto condition = [] { return is_init_pos == false; };
init_cv.wait(lock, condition);
}
// first callback happened, OK to proceed now ...
// More code...
线程将在condition_variable::wait
调用时阻塞,并仅在被通知唤醒后返回,并且看到关联条件的真值(定义为测试布尔变量的lambda表达式)。没有通知或条件为假时,它将继续等待。
编辑: P.S. selbie的类似答案可能会稍微更有效率,因为两个线程之间仅共享g_isReady
变量,因此无需每次仅在第一次调用时就将互斥锁锁定在回调中:
if (is_init_pos)
{
is_init_pos = false;
// Set robot's initial position.
init_pos_x = x;
init_pos_y = y;
std::lock_guard<std::mutex> lock(init_mutex);
g_isReady = true;
init_cv.notify_one();
}
,在main
线程中,使用[] { return g_isReady; };
作为条件。
这使用了一个额外的bool
变量,但避免了在每个回调中锁定互斥量。在实践中,这可能不会有太大变化,所以在我的回答中,我只是重用了您已经拥有的现有is_init_pos
变量。
答案 2 :(得分:0)
在运行多线程应用程序时,其他两个答案是正确的,但这是单线程应用程序。
ROS使用ros::spinOnce()
进行以下操作:
处理一轮回调。
如果您有自己的循环正在运行并且想要 处理任何可用的回调。这相当于 在全局CallbackQueue上调用callAvailable()。它不会 处理已分配给自定义队列的所有回调。
感谢Jonathan Wakely为我指出正确的方向。抱歉,我正在学习,但我不知道。
我终于使用以下代码修复了
://*********************************************
// I want to wait here.
//*********************************************
while (ros::ok)
{
ros::spinOnce();
loop_rate.sleep();
if (!is_init_pos)
break;
}