ROS中多线程

介绍多线程之前先来说下单线程:单线程Spinning

        ros::spin()是最简单的单线程自循, 它会一直调用直到结束
用法: ros::spin(); 也可用ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0.1));
另一个单线程spinning是ros::spinOnce(),它定期调用等待在那个点上的所有回调,
用法: ros::spinOnce(); 也可用ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0));

以上,是它的基础用法,那么spin到底做了什么呢?
首先, 当我们调用ros::spin时, 会有一个互斥锁, 把你的回调队列加锁, 防止执行混乱. 然后, 检测如果回调队列不为空, 则读取回调队列.最后,当while(nh.ok())为true时, 调用当前队列中的所有函数,如果有不满足的, 会重新放回队列中.所以在listener中, 就一直执行这ros::spin来监听话题了.从这样看来,spin和spinOnce的区别之一,就是while(nh::ok())执行块的大小了. 另一个是等待时间, spin在执行时, 会指定一个返回前可以等待调用的时间. spin会等待0.1s而spinonce不会.spinOnce使得pub/sub为非阻塞锁 spin是客户端的, 因此是阻塞的.这样就很好理解talker要用SpinOnce,有需要talk的时候发出,没有的时候不发送.而listener一直在阻塞着听.

这样,再来说之前很流传的一句关于解释spin的话, “所有的回调函数都是spin调用的”. 这是一句形象而不准确的话. 回调函数一直等待在回调队列中, 只要条件一满足就会发生回调, 而spin的作用, 只是创建了线程给这个回调函数去执行它, 这样多线程就不会影响其他的作业.之所以用spin, 是因为rospy不愿指定线程模型, 在程序中将线程暴露出来, 而用spin来把它封装起来. 但你可以用多线程调用任意数量的回调函数.没有用户订阅, 服务和回调是不会被调用的.

介绍完了单线程,再来说下ROS里的多线程就容易理解了----多线程Spinning

        roscpp内部支持调用多线程, 有两个: ros::MultiThreadedSpinner、ros::AsyncSpinner (since 0.10),ros::MultiThreadedSpinner
ros::MultiThreadedSpinner是阻塞式的spinner, 类似于ros::spin(), 你可以在它的构造函数中指定线程数量, 但如果不指定或者设为0, 它会根据你的CPU内核数创建线程.

ros::MultiThreadedSpinner spinner(4); // Use 4 threads
spinner.spin(); // spin() will not return until the node has been shutdown

ros::AsyncSpinner (since 0.10)
一个更有用的线程spinner是AsyncSpinner.不是阻塞式的,类似ros::spinOnce(),拥有start()和stop()两个函数, start()等待在那个点上的所有回调,stop()停止回调,并且在销毁时自动停止.

ros::AsyncSpinner spinner(4); // Use 4 threads
spinner.start();
ros::waitForShutdown();

下面在代码里说明:自定义消息队列处理线程

1.用 ros::CallbackQueue定义需要的回调队列

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <ros/callback_queue.h>

ros::CallbackQueue callback_queue;
void state_callback(const std_msgs::String::ConstPtr& msg)
{
   init_state_flag=1;//也可在需要位置置1
}
int main(int argc,char **argv)
{
   ros::init(argc,argv,"example");

   //monitor the state of the hand.
   ros::NodeHandle nh;

ros::SubscribeOptions ops=ros::SubscribeOptions::create<std_msgs::String>("state",1,
                          state_callback, ros::VoidPtr(),&callback_queue); //指定一个自定义队列

   ros::Subscriber listen_state= nh.subscribe(ops);    // 必须添加,ros::SubscribeOptions自定义的队列才能回调
   
   ros::AsyncSpinner state_spinner(1,&callback_queue);

   while(ros::ok())
   {
       if(init_state_flag == 1)
       {
           state_spinner.stop();
           break;
       }
       state_spinner.start();

}
}

再说下怎么用自己设置的队列替代spin 和spinonce

用setCallbackQueue(&callback_queue)定义需要的回调队列

ros::NodeHandle nh;
nh.setCallbackQueue(&callback_queue);

    1.
    2.

也可以使用NodeHandle NodeHandle::setCallbackQueue(&callback_queue)函数设置自定义队列,这个可使用所有订阅、服务、定时器等。

回调通过callback_queue而不是 roscpp的默认队列.意味着ros::spin()和ros::spinOnce()不会处理这些回调,你需要单独处理这些回调.
(1)通过手工调用 ros::CallbackQueue::callAvailable() 和ros::CallbackQueue::callOne()方法处理。
callavailable()调用队列里所有的,callone()只会调用回调在队列最旧的;callAvailable()和callOne()都会有一个超时选项,在返回前,它会在超时时间内等待回调有效;如果是0,同时队列没有回调,则直接返回。

callback_queue.callAvailable(ros::WallDuration());
callback_queue.callOne(ros::WallDuration()) to only call a single callback instead of all available

    1.
    2.

(2)也可以通过ros::AsyncSpinner spinner(1, &callback_queue);第一个参数代表几个进程,第二个参数代表需要单独处理回调队列.
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值