ROS中的多线程 MultiThreadedSpinner和AsyncSpinner

在ROS当中,原作者是不推荐用多线程的,他建议用多进程,变成一个个节点的形式进行通信。多线程分为两种模式:同步和异步。

  • 同步:MultiThreadSpinner s(4),一共5个线程。包括了主线程。

  • 异步:AsyncSpinner s(4), 一共5个线程。包括了主线程。

回调方法 阻塞 线程
ros::spin() 阻塞 单线程
ros::spinOnce 非阻塞 单线程
ros::MultiThreadedSpinner 阻塞 多线程
ros::AsyncSpinner 非阻塞 多线程

这里讨论的是多线程形式的回调函数,而不是多线程之间如何同步的问题


对于多话题的订阅,我们先看传统的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void cb1(const geometry_msgs::PoseStamped::ConstPtr& msg) 
{
ROS_INFO("uwb_pose x: %f", msg->pose.position.x);
}

void cb2(const geometry_msgs::PoseStamped::ConstPtr& msg)
{
sleep(2);
ROS_INFO("yolo_pose x: %f", msg->pose.position.x);
}

int main(int argc, char **argv)
{
ros::init(argc, argv, "node");
ros::NodeHandle nh;
setlocale(LC_ALL, "");

ros::Subscriber uwbSub = nh.subscribe<geometry_msgs::PoseStamped>("uwb_pose", 1, cb1);
ros::Subscriber yoloSub = nh.subscribe<geometry_msgs::PoseStamped>("yolo_pose", 1, cb2);
ros::spin();
return 1;
}

在回调函数cb2里,可能先执行一大堆耗时的命令,这里用sleep(2)代替,这样cb1ROS_INFO获得的消息就会缺失,这明显就是多线程的问题了。

把代码加上ros::MultiThreadedSpinner s(2); ( 无需加入头文件 ), ros::spin();改为ros::spin(s);, 再运行会发现cb1里没有缺少一个消息。

这里多线程的目的是保证线程cb1不丢失消息,而不是cb2,它丢失消息是必然的。

对于ros::AsyncSpinner,代码在ros::Subscriber定义之后这样写:

1
2
3
ros::AsyncSpinner spinner(2);
spinner.start();
ros::waitForShutdown();

当程序当中有数据处理线程的时候,建议开辟异步多线程订阅,算法写在订阅函数里面。 当然,目前的处理当中,我更倾向于重新开辟一个线程,然后通过循环数组来进行数据交互。

参考:ROS多线程订阅消息