首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >等待主线程直到条件

等待主线程直到条件
EN

Stack Overflow用户
提问于 2019-07-10 10:21:06
回答 3查看 1.5K关注 0票数 1

我在学C++。

我需要等待接收一些数据来启动主线程。

这是回调函数,我将在其中接收我需要的数据:

代码语言:javascript
复制
/**
 * 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;
  }

这是主线:

代码语言:javascript
复制
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...
}

我曾经想过使用like循环来检查is_init_pos是否已经更改,然后继续,但是我需要在while循环中执行一些操作(类似于thread.sleep())。

如何等待接收odomCallback中的第一个“消息”?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-07-11 12:59:11

当您运行多线程应用程序时,其他两个答案是正确的,但这是一个单线程应用程序。

ROS使用ros::spinOnce()来:

处理一轮回调。 如果您有自己的循环运行,并且希望处理任何可用的回调,则此方法非常有用。这相当于在全局callAvailable上调用CallbackQueue ()。它将不会处理分配给自定义队列的任何回调。

感谢乔纳森·韦克利给我指明了正确的方向。抱歉,我在学习,但我不知道。

最后,我使用以下代码进行了修正:

代码语言:javascript
复制
//*********************************************
// I want to wait here.
//*********************************************

while (ros::ok)
{
  ros::spinOnce();
  loop_rate.sleep();

  if (!is_init_pos)
    break;
}
票数 0
EN

Stack Overflow用户

发布于 2019-07-10 11:16:33

全局地,声明一个条件变量和一个在回调线程和main之间共享的互斥体。在互斥锁下的布尔值。

代码语言:javascript
复制
#include <mutex>
#include <condition_variable>
std::condition_variable g_cv;
std::mutex g_mutex;
bool g_isReady = false;

在您的odomCallback内部,在它返回之前,执行以下操作,向main发出信号,表示它可以继续运行

代码语言:javascript
复制
g_mutex.lock();
    g_isReady = true;
g_mutex.unlock();
g_cv.notify_all();

然后,您等待如下:

代码语言:javascript
复制
  //*********************************************
  // 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);
       }
    }
票数 2
EN

Stack Overflow用户

发布于 2019-07-10 11:18:59

编辑:,我不认为你应该使用多线程。通过在主线程“事件循环” 中运行来等待回调。

代码语言:javascript
复制
  //*********************************************
  // I want to wait here.
  //*********************************************
  ros::spin();

请参阅http://wiki.ros.org/roscpp/Overview/Callbacks%20and%20Spinning

原始答案如下:

首先,需要安全地从多个线程并发访问is_init_pos变量。否则,如果主线程在回调线程写入时读取它,则程序将具有未定义的行为。因此,使用互斥锁来同步对布尔值的访问,并在状态更改时使用条件变量发送通知:

代码语言:javascript
复制
bool is_init_pos = true;
std::mutex init_mutex;
std::condition_variable init_cv;

回调函数在访问is_init_pos时应该锁定该互斥对象,并对条件变量发送通知:

代码语言:javascript
复制
  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();
  }

现在,当状态更改时,主线程可以等待条件变量接收通知:

代码语言:javascript
复制
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表达式)。当没有通知或条件为false时,它将继续等待。

编辑: P.S. selbie的类似答案可能会稍微高效一些,因为只有g_isReady变量在两个线程之间共享,所以不需要每次都在回调中锁定互斥对象,只有在第一个调用时:

代码语言:javascript
复制
  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变量。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56968641

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档