当前位置: 首页 > news >正文

Robot Operating System——深度解析单线程执行器(SingleThreadedExecutor)执行逻辑

大纲

  • 创建SingleThreadedExecutor
  • 新增Node
    • add_node
    • trigger_entity_recollect
      • collect_entities
  • 自旋等待
    • get_next_executable
      • wait_for_work
      • get_next_ready_executable
        • Timer
        • Subscription
        • Service
        • Client
        • Waitable
        • AnyExecutable
    • execute_any_executable
  • 参考资料

在ROS2中,我们设置的各种回调都是在执行器(Executor)中执行的,所以它是整个系统非常核心的组件。
在这里插入图片描述
目前,rclcpp 提供了三种 Executor 类型,它们派生自同一个父类Executor。
在这里插入图片描述

本文我们将借用《Robot Operating System——单线程中启动多个Node》中的例子,将单线程执行器在最上层的使用方法总结如下

  // Create an executor that will be responsible for execution of callbacks for a set of nodes.// With this version, all callbacks will be called from within this thread (the main one).rclcpp::executors::SingleThreadedExecutor exec;rclcpp::NodeOptions options;// Add some nodes to the executor which provide work for the executor during its "spin" function.// An example of available work is executing a subscription callback, or a timer callback.auto server = std::make_shared<composition::Server>(options);exec.add_node(server);// spin will block until work comes in, execute work as it becomes available, and keep blocking.// It will only be interrupted by Ctrl-C.exec.spin();

即:

  1. 创建SingleThreadedExecutor ;
  2. 新增Node;
  3. 自旋等待。

创建SingleThreadedExecutor

SingleThreadedExecutor的构造函数基本就是交给基类rclcpp::Executor的构造函数来实现。

// https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/src/rclcpp/executors/single_threaded_executor.cpp
SingleThreadedExecutor::SingleThreadedExecutor(const rclcpp::ExecutorOptions & options)
: rclcpp::Executor(options) {}

在Executor的构造函数中,我们着重关注成员变量collector_。

Executor::Executor(const rclcpp::ExecutorOptions & options)
: spinning(false),interrupt_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),shutdown_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),context_(options.context),notify_waitable_(std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>([this]() {this->entities_need_rebuild_.store(true);})),entities_need_rebuild_(true),collector_(notify_waitable_),wait_set_({}, {}, {}, {}, {}, {}, options.context),current_notify_waitable_(notify_waitable_),impl_(std::make_unique<rclcpp::ExecutorImplementation>())
{shutdown_callback_handle_ = context_->add_on_shutdown_callback([weak_gc = std::weak_ptr<rclcpp::GuardCondition>{shutdown_guard_condition_}]() {auto strong_gc = weak_gc.lock();if (strong_gc) {strong_gc->trigger();}});notify_waitable_->add_guard_condition(interrupt_guard_condition_);notify_waitable_->add_guard_condition(shutdown_guard_condition_);wait_set_.add_waitable(notify_waitable_);
}

collector_是一个集合,它保存了后续要执行的各个Node指针。

/// Collector used to associate executable entities from nodes and guard conditionsrclcpp::executors::ExecutorEntitiesCollector collector_;

新增Node

add_node

业务逻辑的Node会被添加到上面介绍的成员变量collector_中。

// https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/src/rclcpp/executor.cpp
void
Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{this->collector_.add_node(node_ptr);try {this->trigger_entity_recollect(notify);} catch (const rclcpp::exceptions::RCLError & ex) {throw std::runtime_error(std::string("Failed to trigger guard condition on node add: ") + ex.what());}
}

然后会调用trigger_entity_recollect方法。

trigger_entity_recollect

这个方法会做两件事:

  1. 修改std::atomic_bool类型变量entities_need_rebuild_的值为true,进而让collect_entities()被执行。
  2. 如果notify为true,则会通过interrupt_guard_condition_->trigger()唤醒一个处于等待状态的执行器。
// https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/src/rclcpp/executor.cpp
void
Executor::trigger_entity_recollect(bool notify)
{this->entities_need_rebuild_.store(true);if (!spinning.load() && entities_need_rebuild_.exchange(false)) {std::lock_guard<std::mutex> guard(mutex_);this->collect_entities();}if (notify) {interrupt_guard_condition_->trigger();}
}

collect_entities

collect_entities主要做两件事:

  1. 过滤过期的Node以及相关回调函数。
// https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/src/rclcpp/executor.cpp
void
Executor::collect_entities()
{// Updating the entity collection and waitset expires any active resultthis->wait_result_.reset();// Get the current list of available waitables from the collector.rclcpp::executors::ExecutorEntitiesCollection collection;this->collector_.update_collections();auto callback_groups = this->collector_.get_all_callback_groups();rclcpp::executors::build_entities_collection(callback_groups, collection);// We must remove expired entities here, so that we don't continue to use older entities.// See https://github.com/ros2/rclcpp/issues/2180 for more information.current_collection_.remove_expired_entities();
  1. 更新current_collection_和wait_set_。
  // Update each of the groups of entities in the current collection, adding or removing// from the wait set as necessary.current_collection_.timers.update(collection.timers,[this](auto timer) {wait_set_.add_timer(timer);},[this](auto timer) {wait_set_.remove_timer(timer);});current_collection_.subscriptions.update(collection.subscriptions,[this](auto subscription) {wait_set_.add_subscription(subscription, kDefaultSubscriptionMask);},[this](auto subscription) {wait_set_.remove_subscription(subscription, kDefaultSubscriptionMask);});current_collection_.clients.update(collection.clients,[this](auto client) {wait_set_.add_client(client);},[this](auto client) {wait_set_.remove_client(client);});current_collection_.services.update(collection.services,[this](auto service) {wait_set_.add_service(service);},[this](auto service) {wait_set_.remove_service(service);});current_collection_.guard_conditions.update(collection.guard_conditions,[this](auto guard_condition) {wait_set_.add_guard_condition(guard_condition);},[this](auto guard_condition) {wait_set_.remove_guard_condition(guard_condition);});current_collection_.waitables.update(collection.waitables,[this](auto waitable) {wait_set_.add_waitable(waitable);},[this](auto waitable) {wait_set_.remove_waitable(waitable);});// In the case that an entity already has an expired weak pointer// before being removed from the waitset, additionally prune the waitset.this->wait_set_.prune_deleted_entities();
}

自旋等待

spin()内部核心是一个while循环。它会不停使用get_next_executable取出可以运行的Node的回调,然后让execute_any_executable将其执行。

// https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/src/rclcpp/executors/single_threaded_executor.cpp
void
SingleThreadedExecutor::spin()
{if (spinning.exchange(true)) {throw std::runtime_error("spin() called while already spinning");}RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););// Clear any previous result and rebuild the waitsetthis->wait_result_.reset();this->entities_need_rebuild_ = true;while (rclcpp::ok(this->context_) && spinning.load()) {rclcpp::AnyExecutable any_executable;if (get_next_executable(any_executable)) {execute_any_executable(any_executable);}}
}

那么这个while循环会不会导致CPU一直空转呢?答案是:不是。我们可以看get_next_executable的实现。

get_next_executable

// https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/src/rclcpp/executor.cpp
bool
Executor::get_next_executable(AnyExecutable & any_executable, std::chrono::nanoseconds timeout)
{bool success = false;// Check to see if there are any subscriptions or timers needing service// TODO(wjwwood): improve run to run efficiency of this functionsuccess = get_next_ready_executable(any_executable);// If there are noneif (!success) {// Wait for subscriptions or timers to work onwait_for_work(timeout);if (!spinning.load()) {return false;}// Try againsuccess = get_next_ready_executable(any_executable);}return success;
}

它会在底层调用wait_for_work。

wait_for_work

这个方法会一直阻塞到时间超时,或者有回调函数可以被调用。

// https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/src/rclcpp/executor.cpp
void
Executor::wait_for_work(std::chrono::nanoseconds timeout)
{TRACETOOLS_TRACEPOINT(rclcpp_executor_wait_for_work, timeout.count());// Clear any previous wait resultthis->wait_result_.reset();{std::lock_guard<std::mutex> guard(mutex_);if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {this->collect_entities();}}this->wait_result_.emplace(wait_set_.wait(timeout));if (!this->wait_result_ || this->wait_result_->kind() == WaitResultKind::Empty) {RCUTILS_LOG_WARN_NAMED("rclcpp","empty wait set received in wait(). This should never happen.");} else {if (this->wait_result_->kind() == WaitResultKind::Ready && current_notify_waitable_) {auto & rcl_wait_set = this->wait_result_->get_wait_set().get_rcl_wait_set();if (current_notify_waitable_->is_ready(rcl_wait_set)) {current_notify_waitable_->execute(current_notify_waitable_->take_data());}}}
}

其中主要负责等待的是这句

wait_set_.wait(timeout)

在SingleThreadedExecutor中,由于调用get_next_executable没有传递时间,便采用了默认时间。这样get_next_executable会一直等到有回调可以被执行。这样就避免了CPU空转的问题。

  /// Wait for executable in ready state and populate union structure./*** If an executable is ready, it will return immediately, otherwise* block based on the timeout for work to become ready.** \param[out] any_executable populated union structure of ready executable* \param[in] timeout duration of time to wait for work, a negative value*   (the defualt behavior), will make this function block indefinitely* \return true if an executable was ready and any_executable was populated,*   otherwise false*/RCLCPP_PUBLICboolget_next_executable(AnyExecutable & any_executable,std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));

get_next_ready_executable

get_next_ready_executable会按顺序寻找Timer、Subscription、Service 、Client和Waitable中第一个处于可被回调状态的Node。

Timer
bool valid_executable = false;if (!wait_result_.has_value() || wait_result_->kind() != rclcpp::WaitResultKind::Ready) {return false;}if (!valid_executable) {size_t current_timer_index = 0;while (true) {auto [timer, timer_index] = wait_result_->peek_next_ready_timer(current_timer_index);if (nullptr == timer) {break;}current_timer_index = timer_index;auto entity_iter = current_collection_.timers.find(timer->get_timer_handle().get());if (entity_iter != current_collection_.timers.end()) {auto callback_group = entity_iter->second.callback_group.lock();if (!callback_group || !callback_group->can_be_taken_from()) {current_timer_index++;continue;}// At this point the timer is either ready for execution or was perhaps// it was canceled, based on the result of call(), but either way it// should not be checked again from peek_next_ready_timer(), so clear// it from the wait result.wait_result_->clear_timer_with_index(current_timer_index);// Check that the timer should be called still, i.e. it wasn't canceled.any_executable.data = timer->call();if (!any_executable.data) {current_timer_index++;continue;}any_executable.timer = timer;any_executable.callback_group = callback_group;valid_executable = true;break;}current_timer_index++;}}
Subscription
  if (!valid_executable) {while (auto subscription = wait_result_->next_ready_subscription()) {auto entity_iter = current_collection_.subscriptions.find(subscription->get_subscription_handle().get());if (entity_iter != current_collection_.subscriptions.end()) {auto callback_group = entity_iter->second.callback_group.lock();if (!callback_group || !callback_group->can_be_taken_from()) {continue;}any_executable.subscription = subscription;any_executable.callback_group = callback_group;valid_executable = true;break;}}}
Service
  if (!valid_executable) {while (auto service = wait_result_->next_ready_service()) {auto entity_iter = current_collection_.services.find(service->get_service_handle().get());if (entity_iter != current_collection_.services.end()) {auto callback_group = entity_iter->second.callback_group.lock();if (!callback_group || !callback_group->can_be_taken_from()) {continue;}any_executable.service = service;any_executable.callback_group = callback_group;valid_executable = true;break;}}}
Client
  if (!valid_executable) {while (auto client = wait_result_->next_ready_client()) {auto entity_iter = current_collection_.clients.find(client->get_client_handle().get());if (entity_iter != current_collection_.clients.end()) {auto callback_group = entity_iter->second.callback_group.lock();if (!callback_group || !callback_group->can_be_taken_from()) {continue;}any_executable.client = client;any_executable.callback_group = callback_group;valid_executable = true;break;}}}
Waitable
  if (!valid_executable) {while (auto waitable = wait_result_->next_ready_waitable()) {auto entity_iter = current_collection_.waitables.find(waitable.get());if (entity_iter != current_collection_.waitables.end()) {auto callback_group = entity_iter->second.callback_group.lock();if (!callback_group || !callback_group->can_be_taken_from()) {continue;}any_executable.waitable = waitable;any_executable.callback_group = callback_group;any_executable.data = waitable->take_data();valid_executable = true;break;}}}
AnyExecutable
// https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/include/rclcpp/any_executable.hpp
struct AnyExecutable
{RCLCPP_PUBLICAnyExecutable();RCLCPP_PUBLICvirtual ~AnyExecutable();// Only one of the following pointers will be set.rclcpp::SubscriptionBase::SharedPtr subscription;rclcpp::TimerBase::SharedPtr timer;rclcpp::ServiceBase::SharedPtr service;rclcpp::ClientBase::SharedPtr client;rclcpp::Waitable::SharedPtr waitable;// These are used to keep the scope on the containing itemsrclcpp::CallbackGroup::SharedPtr callback_group {nullptr};rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base {nullptr};std::shared_ptr<void> data {nullptr};
};

execute_any_executable

找到可以执行的Node后,便可以调用execute_any_executable让其执行。

在execute_any_executable内部,我们看到它也是区分Timer、Subscription、Service 、Client和Waitable类型来执行的。

// https://github.com/ros2/rclcpp/blob/jazzy/rclcpp/src/rclcpp/executor.cpp
void
Executor::execute_any_executable(AnyExecutable & any_exec)
{if (!spinning.load()) {return;}assert((void("cannot execute an AnyExecutable without a valid callback group"),any_exec.callback_group));if (any_exec.timer) {TRACETOOLS_TRACEPOINT(rclcpp_executor_execute,static_cast<const void *>(any_exec.timer->get_timer_handle().get()));execute_timer(any_exec.timer, any_exec.data);}if (any_exec.subscription) {TRACETOOLS_TRACEPOINT(rclcpp_executor_execute,static_cast<const void *>(any_exec.subscription->get_subscription_handle().get()));execute_subscription(any_exec.subscription);}if (any_exec.service) {execute_service(any_exec.service);}if (any_exec.client) {execute_client(any_exec.client);}if (any_exec.waitable) {const std::shared_ptr<void> & const_data = any_exec.data;any_exec.waitable->execute(const_data);}// Reset the callback_group, regardless of typeany_exec.callback_group->can_be_taken_from().store(true);
}

参考资料

  • https://docs.ros.org/en/rolling/Concepts/Intermediate/About-Executors.html#executors

相关文章:

Robot Operating System——深度解析单线程执行器(SingleThreadedExecutor)执行逻辑

大纲 创建SingleThreadedExecutor新增Nodeadd_nodetrigger_entity_recollectcollect_entities 自旋等待get_next_executablewait_for_workget_next_ready_executableTimerSubscriptionServiceClientWaitableAnyExecutable execute_any_executable 参考资料 在ROS2中&#xff0c…...

【TS】使用npm全局安装typescript

查看npm安装 npm -v 安装typescript npm i -g typescript 查看安装 tsc 这就是标致着安装完成。...

安全用户角色权限

$PATH 搞系统设置设置⾥头path ⽬标包含mysql 可执⾏⽂件&#xff0c;那么就是由使⽤ 在终端使⽤ ./bin/mysql -h192.168.71.164 -P3306 -uroot -proot 1.远程登录前提条件是mysql.user表中的host属性为%&#xff0c;如果是 localhost就不允许远程登录&#xff0c;update…...

代理模式学习

代理模式 代理模式是常用的java设计模式&#xff0c;他的特征是代理类与委托类有同样的接口&#xff0c;代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类&#xff0c;以及事后处理消息等。代理类与委托类之间通常会存在关联关系&#xff0c;一个代理类的对象…...

深入理解Go 语言信号量 Semaphore

1. 什么是信号量 信号量的概念是荷兰计算机科学家 1.1 P/V 操作 Dijkstra 在他的论文中为信号量定义了两个操作 : P 和 V 。 1.2 信号量和互斥锁的区别与联系 信号量有两种类型&#xff1a;二元信号量和计数信号量。 2. 信号量的 channel 实现 程序在运行时&#xff0c;…...

VisualStudio2019下载与安装

1.下载 通过百度网盘分享的文件&#xff1a;VisualStudio2019 链接&#xff1a;https://pan.baidu.com/s/16tqm0ZsOkmXTfGmi4LnGbA 提取码&#xff1a;wx60 --来自百度网盘超级会员V3的分享 2.安装...

李宏毅老师机器学习常见英语词汇

目录 1.Regression &#xff1a;回归2.Classification&#xff1a;分类3.local minima:局部最小值4.saddle point:鞍点5.ground truth:它是机器学习算法的参考标准&#xff0c;用于衡量模型的性的和判断模型的准确性6.optimization:优化 1.Regression &#xff1a;回归 2.Clas…...

人工智能时代,程序员如何保持核心竞争力?

人工智能时代&#xff0c;程序员如何保持核心竞争力&#xff1f; 随着AIGC&#xff08;如chatgpt、midjourney、claude等&#xff09;大语言模型接二连三的涌现&#xff0c;AI辅助编程工具日益普及&#xff0c;程序员的工作方式正在发生深刻变革。有人担心AI可能取代部分编程工…...

WiFi to Ethernet: 树莓派共享无线连接至有线网口,自动通过Captive Poartal网页登录认证

物联网开发系列&#xff1a;物联网开发之旅① WiFi to Ethernet: 树莓派共享无线连接至有线网口&#xff0c;自动通过Captive Poartal验证物联网开发番外篇之 Captive Portal验证原理 文章目录 背景实现工具实现细节一、将无线连接共享到以太网1. 配置静态IP地址2. 启用IP转发3…...

【神软大数据治理平台-高级动态SQL(接口开发)】

1、背景 业务部门需大数据平台按照所提需求提供企业数据接口&#xff0c;基于神软大数据治理平台-高级动态SQL功能&#xff0c;满足业务需求&#xff0c;如下&#xff1a; &#xff08;1&#xff09;业务系统需求&#xff1a; 输入&#xff1a; enterpriseName&#xff1a;…...

【Java数据结构】Map和Set超详细两万字讲解(内含搜索树+哈希表)

&#x1f512;文章目录&#xff1a; 1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; 2. Map和Set的基础概念 3.Map的基础使用 4.Set的基础使用 5. TreeMap的本质——红黑树 5.1二叉搜索树的概念 5.2二叉搜索树的模拟实现 二叉搜索树——查找 二…...

中国制造2025,会抛弃精益生产吗?

时至今日&#xff0c;“精益生产”模式依旧大行其道&#xff0c;它始终支持着中国制造业以最低的成本做出优质产品。我们认为&#xff0c;纵然是中国制造2025成为现实&#xff0c;精益生产模式也仍然是整个制造业的精髓之一。 首先&#xff0c;精益生产模式最重要的一根脊梁就是…...

Rust 循环

Rust 循环 在编程语言中&#xff0c;循环是一种重要的控制结构&#xff0c;它允许我们重复执行一段代码直到满足特定的条件。Rust 语言提供了多种循环方式&#xff0c;每种方式都有其特定的用途和语法。本文将详细介绍 Rust 中的循环&#xff0c;包括 loop、while、while let、…...

数据结构(其四)--特殊矩阵的存储

目录 11.特殊矩阵的压缩存储 &#xff08;1&#xff09;.一维数组的储存结构 &#xff08;2&#xff09;.二维数组的存储结构 &#xff08;3&#xff09;.普通矩阵的存储 &#xff08;4&#xff09;.特殊矩阵的压缩存储 i.对称矩阵 ii.三角矩阵 iii.三对角矩阵 iiii.稀疏矩…...

系统化学习 H264视频编码(06)哥伦布编码

说明&#xff1a;我们参考黄金圈学习法&#xff08;什么是黄金圈法则?->模型 黄金圈法则&#xff0c;本文使用&#xff1a;why-what&#xff09;来学习音H264视频编码。本系列文章侧重于理解视频编码的知识体系和实践方法&#xff0c;理论方面会更多地讲清楚 音视频中概念的…...

手机在网状态接口如何对接?(一)

一、什么是手机在网状态&#xff1f; 传入手机号码&#xff0c;查询该手机号的在网状态&#xff0c;返回内容有正常使用、停机、在网但不可用、不在网&#xff08;销号/未启用/异常&#xff09;、预销户等多种状态。 二、手机在网状态使用场景&#xff1f; 1.信贷审核&#…...

数据结构链表2(常考习题1)(C语言)

移除链表元素&#xff1a; . - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 解题思路&#xff1a; 情况1&#xff1a; 情…...

Rust的运行时多态

Rust的运行时多态 Rust的静态多态即编译时多态&#xff0c;通过**泛型特征约束&#xff08;Generic Type Trait Constrait&#xff09;**来实现&#xff1b; 那么动态多态&#xff08;运行时多态&#xff09;呢&#xff1f;答案是特征对象&#xff08;Trait Object&#xff…...

sqllabs通关

sqllabs5:(报错注入) ?id1 回显You are in........... ?id2-1 回显You are in........... ?id1 回显 1 LIMIT 0,1 判断是字符型&#xff0c;闭合。?id1order by 3-- //页面显示正常我们试了4行得出是报错注入 我们先爆库名 http://127.0.0.1/sqli-labs-master/L…...

RTSP系列四:RTSP Server/Client实战项目

RTSP系列&#xff1a; RTSP系列一&#xff1a;RTSP协议介绍-CSDN博客 RTSP系列二&#xff1a;RTSP协议鉴权-CSDN博客 RTSP系列三&#xff1a;RTP协议介绍-CSDN博客 RTSP系列四&#xff1a;RTSP Server/Client实战项目-CSDN博客 目录 一、RTSP Server实战项目 1、准备 2、…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

Linux中《基础IO》详细介绍

目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改&#xff0c;实现简单cat命令 输出信息到显示器&#xff0c;你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...

Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解

文章目录 一、开启慢查询日志&#xff0c;定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...