等待主线程直到状态

时间:2019-07-10 10:21:06

标签: c++ multithreading

我正在学习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中收到第一个“消息”?

3 个答案:

答案 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;
}