ros2 单线程与多线程

rclcpp::executors::SingleThreadedExecutor executor_;

在 ROS 2(Robot Operating System 2)中,rclcpp::executors::SingleThreadedExecutor 是一个常用的执行器类,用于处理节点的回调函数和事件。

你提到的 executor_ 看起来是一个 SingleThreadedExecutor 类型的成员变量。这种执行器使用单个线程来处理所有注册到它的节点的回调函数,按照它们被触发的顺序依次执行。

在实际使用中,通常的模式是:

  1. 创建一个或多个节点
  2. 将这些节点添加到执行器中
  3. 调用执行器的 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);
  
  // 创建多线程执行器,指定线程数为4
  rclcpp::executors::MultiThreadedExecutor executor(rclcpp::executor::ExecutorArgs(), 4);
  
  // 创建节点
  auto node = std::make_shared<MyNode>();
  
  // 将节点添加到执行器
  executor.add_node(node);
  
  // 开始执行回调
  executor.spin();
  
  rclcpp::shutdown();
  return 0;
}

主要区别和注意事项:

  1. MultiThreadedExecutor 可以通过构造函数参数指定线程数量

  2. // 方式1:直接指定线程数
    rclcpp::executors::MultiThreadedExecutor executor(4);
    
    // 方式2:使用ExecutorArgs结构体
    rclcpp::executor::ExecutorArgs args;
    args.number_of_threads = 4;
    rclcpp::executors::MultiThreadedExecutor executor(args);
    
  3. 多线程执行器会在不同线程上并行执行回调函数,因此需要注意:

    • 共享数据的线程安全问题
    • 可能需要使用互斥锁 (std::mutex) 保护共享资源
    • 回调函数的执行顺序不再有严格保证
  4. 线程数量的选择应根据实际需求:

    • 过多的线程可能导致系统开销增加
    • 通常根据 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的线程池大小,以达到最佳性能。

### ROS2多线程的使用方法 在 ROS2 的架构下,为了提高系统的并发性能和响应速度,引入了更灵活的多线程机制。不同于 ROS1,在 C++ 和 Python 下的不同行为模式,ROS2 提供了一致性的多线程支持[^3]。 #### 创建多线程节点 要创建一个多线程ROS2 节点,可以通过设置 `rclcpp::NodeOptions` 对象来指定执行器(Executor)。默认情况下,ROS2 使用单线程执行器;然而,当涉及到多个回调函数时,推荐使用多线程执行器以提升效率: ```cpp #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto options = rclcpp::NodeOptions(); // 设置为多线程执行器 rclcpp::executors::MultiThreadedExecutor exec; std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("multi_thread_node", options); // 添加更多逻辑... } ``` #### 配置执行器类型 除了上述提到的方法外,还可以直接配置执行器类型的参数来进行初始化工作。这允许开发者更加精细地控制哪些部分应该采用何种形式的工作流策略: ```cpp // 初始化多线程执行器实例 auto executor = rclcpp::executors::MultiThreadedExecutor(); // 将节点加入到执行器中 executor.add_node(node); while (true){ try { // 执行所有已注册的任务直到遇到错误为止 executor.spin_some(); } catch (...) { /* 错误处理 */ } } ``` #### 线程安全注意事项 尽管 ROS2 支持多线程环境下的开发,但在编写代码过程中仍需注意数据竞争条件等问题。确保共享资源访问的安全性至关重要,比如利用互斥锁或其他同步原语保护临界区内的操作。 #### 实际案例分析 考虑到实际应用场景的需求,例如传感器读取、消息发布订阅等功能模块之间的协作关系,合理规划各组件间的交互方式能够有效减少延迟并增强稳定性。对于复杂度较高的项目来说,适当增加线程数量有助于分担计算负担,从而获得更好的实时表现效果[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值