ros2 单线程与多线程
rclcpp::executors::SingleThreadedExecutor executor_;
在 ROS 2(Robot Operating System 2)中,rclcpp::executors::SingleThreadedExecutor
是一个常用的执行器类,用于处理节点的回调函数和事件。
你提到的 executor_
看起来是一个 SingleThreadedExecutor
类型的成员变量。这种执行器使用单个线程来处理所有注册到它的节点的回调函数,按照它们被触发的顺序依次执行。
在实际使用中,通常的模式是:
- 创建一个或多个节点
- 将这些节点添加到执行器中
- 调用执行器的
spin()
方法开始处理回调
示例代码片段:
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"class MyNode : public rclcpp::Node {// 节点实现...
};int main(int argc, char * argv[]) {rclcpp::init(argc, argv);// 创建执行器rclcpp::executors::SingleThreadedExecutor executor_;// 创建节点auto node = std::make_shared<MyNode>();// 将节点添加到执行器executor_.add_node(node);// 开始执行回调executor_.spin();rclcpp::shutdown();return 0;
}
SingleThreadedExecutor
适用于不需要并行处理回调的简单场景,它可以避免多线程带来的同步问题,但在处理大量计算密集型回调时可能会导致延迟。
rclcpp::executors::SingleThreadedExecutor executor_; 添加多线程
如果你想在 ROS 2 中使用多线程处理回调,可以将SingleThreadedExecutor
替换为MultiThreadedExecutor
,它支持配置线程池大小来实现多线程处理回调。
以下是使用多线程执行器的示例代码:
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors/multi_threaded_executor.hpp"class MyNode : public rclcpp::Node {
public:MyNode() : Node("my_node") {// 创建一个定时器回调作为示例timer_ = this->create_wall_timer(std::chrono::milliseconds(100),std::bind(&MyNode::timer_callback, this));// 创建一个订阅者作为示例subscriber_ = this->create_subscription<std_msgs::msg::String>("test_topic",10,std::bind(&MyNode::sub_callback, this, std::placeholders::_1));}private:void timer_callback() {RCLCPP_INFO(this->get_logger(), "Timer callback executed on thread: %d", std::this_thread::get_id());}void sub_callback(const std_msgs::msg::String::SharedPtr msg) {RCLCPP_INFO(this->get_logger(), "Subscriber callback executed on thread: %d",std::this_thread::get_id());}rclcpp::TimerBase::SharedPtr timer_;rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscriber_;
};int main(int argc, char * argv[]) {rclcpp::init(argc, argv);// 创建多线程执行器,指定线程数为4rclcpp::executors::MultiThreadedExecutor executor(rclcpp::executor::ExecutorArgs(), 4);// 创建节点auto node = std::make_shared<MyNode>();// 将节点添加到执行器executor.add_node(node);// 开始执行回调executor.spin();rclcpp::shutdown();return 0;
}
主要区别和注意事项:
MultiThreadedExecutor
可以通过构造函数参数指定线程数量// 方式1:直接指定线程数 rclcpp::executors::MultiThreadedExecutor executor(4);// 方式2:使用ExecutorArgs结构体 rclcpp::executor::ExecutorArgs args; args.number_of_threads = 4; rclcpp::executors::MultiThreadedExecutor executor(args);
多线程执行器会在不同线程上并行执行回调函数,因此需要注意:
- 共享数据的线程安全问题
- 可能需要使用互斥锁 (
std::mutex
) 保护共享资源 - 回调函数的执行顺序不再有严格保证
线程数量的选择应根据实际需求:
- 过多的线程可能导致系统开销增加
- 通常根据 CPU 核心数量来设置合理的线程数
使用多线程执行器可以提高系统的响应性,特别是当某些回调函数执行时间较长时,可以避免阻塞其他回调的执行。
如何配置MultiThreadedExecutor的线程池大小?
在 ROS 2 中,配置MultiThreadedExecutor
的线程池大小有几种常用方法,可根据具体需求选择:
1. 直接通过构造函数指定线程数
最简单的方式是在创建MultiThreadedExecutor
对象时直接指定线程数量:
2. 使用ExecutorArgs
结构体配置
更灵活的方式是通过rclcpp::executor::ExecutorArgs
结构体进行配置,除了线程数外还能设置其他参数:
. 动态设置(基于 CPU 核心数)
通常建议根据系统 CPU 核心数量来设置线程数,可通过std::thread::hardware_concurrency()
获取:
. 从参数服务器读取配置
更灵活的方式是从 ROS 参数服务器读取线程数配置,便于运行时调整:
// 先创建一个临时节点用于读取参数
auto temp_node = rclcpp::Node::make_shared("executor_config_node");// 声明参数(默认值为4)
temp_node->declare_parameter("executor_threads", 4);// 读取参数
int thread_count;
temp_node->get_parameter("executor_threads", thread_count);// 创建执行器
rclcpp::executors::MultiThreadedExecutor executor(thread_count);
注意事项:
- 线程数并非越多越好,过多的线程会导致线程切换开销增大
- 对于 CPU 密集型任务,线程数通常设置为 CPU 核心数的 1-2 倍
- 对于 IO 密集型任务(如网络通信、传感器读取),可适当增加线程数
- 多线程执行器会并行执行回调,需确保回调函数中的共享资源线程安全(可使用
std::mutex
等同步机制)
通过上述方法,你可以根据应用场景灵活配置MultiThreadedExecutor
的线程池大小,以达到最佳性能。