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来把它封装起来. 但你可以用多线程调用任意数量的回调函数.没有用户订阅, 服务和回调是不会被调用的.
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();
}
}
2.用setCallbackQueue(&callback_queue)定义需要的回调队列
ros::NodeHandle nh;
nh.setCallbackQueue(&callback_queue);
也可以使用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
(2)也可以通过ros::AsyncSpinner spinner(1, &callback_queue);第一个参数代表几个进程,第二个参数代表需要单独处理回调队列.