【开源】C++ 周期任务调度的思想和实现


今天调休,抓住年假的最后一天,将构思多日适合将并行任务串行执行的框架实现出来。

核心思想:

  • 将各个独立的功能模块作为周期性的任务。
  • 在主循环集中调度所有任务,让各个功能模块依次有处理事项的机会。
  • 如果处理事项较为耗时,可以借助 std::async() 的方式通过新线程处理。

设计优点:

  • 可以设置调度间隔时长,便于用于需要定时执行某些事项的模块。
  • 各个任务可以动态添加或移除,实现可动态开启或关闭指定的功能模块。
  • 各个任务可以动态调整间隔时长,实现可动态调整功能模块的执行周期。
  • 采用继承接口的方式,将调度执行和增加事项分离,很方便的实现缓存执行的任务。
    如需要下载多个文件,可以将下载请求放入列表中,在每次被调度时从列表中取出一条请求执行。

实现的代码很简单:

/**
******************************************************************************
* @文件 PeriodTask.hpp
* @版本 V1.0.0
* @日期
* @概要 管理和执行需要周期性调度的工作任务
* @原作 lmx 1007566569@qq.com
******************************************************************************
* @注意
*
******************************************************************************
*/#ifndef __PERIOD_TASK_H__
#define __PERIOD_TASK_H__#include <list>
#include <mutex>
#include <memory>
#include <algorithm>
#include "MeasureTime.hpp"class PeriodTask 	// 用于需要被周期性调度的对象继承
{
public:virtual ~PeriodTask() {};/*********************************************************************** 函数: addPeriodTask* 功能: 被加入调度时的回调, 可初始化所需要的资源* 参数: 无需参数* 返回: 无返回值* 注意: 执行时间过长会影响调度, 因此执行时间需要尽量短**********************************************************************/	virtual void addPeriodTask(void){}; /*********************************************************************** 函数: delPeriodTask* 功能: 被移除调度时的回调, 可以释放所占用的资源* 参数: 无需参数* 返回: 无返回值**********************************************************************/	virtual void delPeriodTask(void){};/*********************************************************************** 函数: runPeriodTask* 功能: 被调度时的回调, 必须被实现, 执行时间需要尽可能短* 参数: 无需参数* 返回: 无返回值**********************************************************************/	virtual void runPeriodTask(void) = 0;	// 被调度执行
};class PeriodTasks	// 用于管理和调度对象
{
private:class Task {	// 用于组织调度对象, 为调度对象支持新特性private:std::shared_ptr<PeriodTask>  sp_task_;std::shared_ptr<MeasureTime> sp_time_;public:/*********************************************************************** 函数: 构造方法* 功能: 组合调度对象和超时参数* 参数: sp_task:调度对象	interval_msec:调度间隔时长(毫秒)* 返回: 无返回值**********************************************************************/	Task(std::shared_ptr<PeriodTask> sp_task, unsigned long long interval_msec = 0){sp_task_ = sp_task;sp_time_ = interval_msec ? std::make_shared<MeasureTime>(interval_msec) : nullptr;}/*********************************************************************** 函数: getTask* 功能: 获取调度对象* 参数: 无需参数* 返回: 返回调度对象**********************************************************************/	std::shared_ptr<PeriodTask> getTask(){ return sp_task_;}/*********************************************************************** 函数: getIntervalTime* 功能: 获取间隔时间* 参数: 无需参数* 返回: 返回间隔时间**********************************************************************/	unsigned long long getIntervalTime(){return sp_time_ ? sp_time_->getTimeout() : 0;}/*********************************************************************** 函数: isReady* 功能: 该调度对象是否准备好被调度* 参数: 无需参数* 返回: true:已经准备就绪 false:指定间隔时间还未到**********************************************************************/	bool isReady(){if(sp_time_ && sp_time_->isTimeout()){sp_time_->update();return true;}return nullptr != sp_time_ ? false : true;}};private:std::mutex mutex_task_;std::list<std::shared_ptr<Task>> task_list_;public:/*********************************************************************** 函数: ~PeriodTasks* 功能: 析构方法, 清除所有任务* 参数: 无需参数* 返回: 无返回值**********************************************************************/	~PeriodTasks(){std::unique_lock< std::mutex > lock(mutex_task_);for (auto task = task_list_.begin(); task != task_list_.end();) {(*task)->getTask()->delPeriodTask();task = task_list_.erase(task);}}/*********************************************************************** 函数: isTask* 功能: 判断指定调度对象是否在调度列表中* 参数: sp_task:调度对象* 返回: true:已在列表 		false:未在列表**********************************************************************/	bool isTask(std::shared_ptr<PeriodTask> sp_task){std::unique_lock< std::mutex > lock(mutex_task_);if(std::find_if(task_list_.begin(), task_list_.end(), [sp_task](std::shared_ptr<Task> t) { return t->getTask() == sp_task; }) != task_list_.end()){return true;}return false;}/*********************************************************************** 函数: addTask* 功能: 将指定调度对象加入调度列表中, 会触发 addPeriodTask()* 参数: sp_task:调度对象	interval_msec:调度间隔时长(毫秒)* 返回: true:加入成功 		false:加入失败,列表已有**********************************************************************/	bool addTask(std::shared_ptr<PeriodTask> sp_task, unsigned long long interval_msec = 0) {std::unique_lock< std::mutex > lock(mutex_task_);if(std::find_if(task_list_.begin(), task_list_.end(), [sp_task](std::shared_ptr<Task> t) { return t->getTask() == sp_task; }) != task_list_.end()){return false;}sp_task->addPeriodTask();task_list_.push_back(std::make_shared<Task>(sp_task, interval_msec));task_list_.sort([](std::shared_ptr<Task> a, std::shared_ptr<Task> b){return a->getIntervalTime() < b->getIntervalTime();});return true;}/*********************************************************************** 函数: delTask* 功能: 从调度列表中删除指定调度对象, 会触发 delPeriodTask()* 参数: sp_task:调度对象* 返回: true:删除成功		false:删除失败,列表未找到**********************************************************************/	bool delTask(std::shared_ptr<PeriodTask> sp_task) {std::unique_lock< std::mutex > lock(mutex_task_);for (auto task = task_list_.begin(); task != task_list_.end(); task++) {if ((*task)->getTask() == sp_task) {task_list_.erase(task);lock.unlock();sp_task->delPeriodTask();return true;}}return false;}/*********************************************************************** 函数: modTask* 功能: 从调度列表中修改指定调度对象的间隔时间* 参数: sp_task:调度对象	interval_msec:新的间隔时间(毫秒级)* 返回: true:修改成功		false:修改失败,列表未找到**********************************************************************/	bool modTask(std::shared_ptr<PeriodTask> sp_task, unsigned long long interval_msec){if(isTask(sp_task) == true){std::unique_lock< std::mutex > lock(mutex_task_);task_list_.remove_if([sp_task](std::shared_ptr<Task> &t) { return t->getTask() == sp_task; });task_list_.push_back(std::make_shared<Task>(sp_task, interval_msec));task_list_.sort([](std::shared_ptr<Task> a, std::shared_ptr<Task> b){return a->getIntervalTime() < b->getIntervalTime();});return true;}return false;}/*********************************************************************** 函数: runTask* 功能: 开始调度所有的对象* 参数: 无需参数* 返回: 无返回值**********************************************************************/	void runTasks(void) {std::unique_lock< std::mutex > lock(mutex_task_);for (auto task = task_list_.begin(); task != task_list_.end(); task++) {if((*task)->isReady()){(*task)->getTask()->runPeriodTask();}}}
};#endif

为了支持任务可指定间隔时长,实现时间测量的工具类,独立出来也方便需要的时候引用。

/**
******************************************************************************
* @文件 MarkTime.hpp
* @版本 V1.0.0
* @日期
* @概要 用于标记和测量时间
* @原作 lmx 1007566569@qq.com
******************************************************************************
* @注意
*
******************************************************************************
*/#ifndef __MARK_TIME_H__
#define __MARK_TIME_H__#include <mutex>
#include <time.h>class MarkTime
{
private:std::mutex mutex_data_;struct timespec timespec_;public:/*********************************************************************** 函数: MarkTime* 功能: 构造方法, 初始化时间基准* 参数: 无需参数* 返回: 无返回值**********************************************************************/MarkTime(){clock_gettime(CLOCK_MONOTONIC_RAW, &timespec_);}/*********************************************************************** 函数: mark* 功能: 记录当前时间* 参数: 无需参数* 返回: 无返回值**********************************************************************/inline void mark(){std::unique_lock< std::mutex > lock(mutex_data_);clock_gettime(CLOCK_MONOTONIC_RAW, &timespec_);	}/*********************************************************************** 函数: getMicrosecond* 功能: 获取自标记起, 调用此接口时的时间* 参数: 无需参数* 返回: 返回自标记到调用此接口时的时间(微秒级)**********************************************************************/	inline unsigned long long getMicrosecond(){struct timespec timespec;clock_gettime(CLOCK_MONOTONIC_RAW, &timespec);std::unique_lock< std::mutex > lock(mutex_data_);return ((timespec.tv_sec * 1000000) + (timespec.tv_nsec / 1000.0)) - ((timespec_.tv_sec * 1000000) + (timespec_.tv_nsec / 1000.0));}/*********************************************************************** 函数: getMillisecond* 功能: 获取自标记起, 调用此接口时的时间* 参数: 无需参数* 返回: 返回自标记到调用此接口时的时间(毫秒级)**********************************************************************/		inline unsigned long long getMillisecond(){struct timespec timespec;clock_gettime(CLOCK_MONOTONIC_RAW, &timespec);std::unique_lock< std::mutex > lock(mutex_data_);return ((timespec.tv_sec * 1000) + (timespec.tv_nsec / 1000000.0)) - ((timespec_.tv_sec * 1000) + (timespec_.tv_nsec / 1000000.0));}
};#endif
/**
******************************************************************************
* @文件 MeasureTime.hpp
* @版本 V1.0.0
* @日期
* @概要 用于计时
* @原作 lmx 1007566569@qq.com
******************************************************************************
* @注意
*
******************************************************************************
*/#ifndef __MEASURE_TIME_H__
#define __MEASURE_TIME_H__#include <mutex>
#include <memory>
#include "base/MarkTime.hpp"class MeasureTime
{
private:bool is_activate_;std::mutex mutex_data_;unsigned long long timeout_;std::shared_ptr<MarkTime> sp_marktime_;public:/*********************************************************************** 函数: MeasureTime* 功能: 初始化超时时间* 参数: timeout: 超时时间(毫秒级)* 返回: 无返回值**********************************************************************/		MeasureTime(unsigned long long timeout){timeout_ = timeout;is_activate_ = false;sp_marktime_ = std::make_shared<MarkTime>();}/*********************************************************************** 函数: get* 功能: 获取标记时间对象* 参数: 无需参数* 返回: 返回标记时间对象**********************************************************************/	std::shared_ptr<MarkTime> get(){return sp_marktime_;}/*********************************************************************** 函数: setTimeout* 功能: 设置超时时间* 参数: timeout:超时时间* 返回: 无返回值**********************************************************************/	void setTimeout(unsigned long long timeout){timeout_ = timeout;}/*********************************************************************** 函数: getTimeout* 功能: 获取超时时间* 参数: 无需参数* 返回: 返回设置的超时时间**********************************************************************/	unsigned long long getTimeout(){return timeout_;}/*********************************************************************** 函数: update* 功能: 更新时间* 参数: 无需参数* 返回: 无返回值**********************************************************************/	inline void update(){sp_marktime_->mark();}/*********************************************************************** 函数: isTimeout* 功能: 判断当前是否超时* 参数: 无需参数* 返回: true:已经超出指定超时时间  false:尚未超出指定超时时间**********************************************************************/	inline bool isTimeout(){return sp_marktime_->getMillisecond() >= timeout_;}/*********************************************************************** 函数: isNotTimeout* 功能: 判断当前是否没有超时* 参数: 无需参数* 返回: true:尚未超出指定超时时间  false:已经超出指定超时时间**********************************************************************/	inline bool isNotTimeout(){return sp_marktime_->getMillisecond() < timeout_;}/*********************************************************************** 函数: isActivate* 功能: 判断当前计时器是否激活* 参数: 无需参数* 返回: true:计时器已经被激活  false:计时器未被激活**********************************************************************/	inline bool isActivate(){ std::unique_lock< std::mutex > lock(mutex_data_);return is_activate_; }/*********************************************************************** 函数: setActivate* 功能: 激活计时器* 参数: 无需参数* 返回: 无返回值**********************************************************************/	inline void setActivate() { std::unique_lock< std::mutex > lock(mutex_data_);is_activate_ = true; }/*********************************************************************** 函数: setActivateAndUpdate* 功能: 激活计时器, 并更新时间* 参数: 无需参数* 返回: 无返回值**********************************************************************/	inline void setActivateAndUpdate() { std::unique_lock< std::mutex > lock(mutex_data_);is_activate_ = true; sp_marktime_->mark();}/*********************************************************************** 函数: setCancelActivate* 功能: 取消激活计时器* 参数: 无需参数* 返回: 禁用定时器前的计时器状态**********************************************************************/inline bool setCancelActivate(){std::unique_lock< std::mutex > lock(mutex_data_);bool is_activate = is_activate_;is_activate_ = false;return is_activate;}/*********************************************************************** 函数: isActivateAndTimeout* 功能: 判断当前计时器是否被激活,如果被激活是否已经超时* 参数: 无需参数* 返回: true:计时器已被激活且已经超时  false:计时器未激活或者没有超时**********************************************************************/	inline bool isActivateAndTimeout(){std::unique_lock< std::mutex > lock(mutex_data_);return is_activate_ ? isTimeout() : false;}/*********************************************************************** 函数: isActivateAndNotTimeout* 功能: 判断当前计时器是否被激活,如果被激活是否没有超时* 参数: 无需参数* 返回: true:计时器已被激活且没有超时  false:计时器未激活或者已经超时**********************************************************************/	inline bool isActivateAndNotTimeout(){std::unique_lock< std::mutex > lock(mutex_data_);return is_activate_ ? isNotTimeout() : false;}/*********************************************************************** 函数: setIfActivateAndTimeoutToCancel* 功能: 如果定时器激活并且超时了, 则取消激活定时器, 返回 true* 参数: 无需参数* 返回: true:定时器被激活且超时了(会禁用了定时器) false:计时器未激活或者没有超时**********************************************************************/inline bool setIfActivateAndTimeoutToCancel(){std::unique_lock< std::mutex > lock(mutex_data_);bool is_activate = is_activate_ && isTimeout();is_activate_ = is_activate ? false : is_activate_;return is_activate;}};#endif

测试用例:

#include <future>
#include <unistd.h>
#include <functional>
#include "PeriodTask.hpp"
#include "base/MarkTime.hpp"class Task1 : public PeriodTask { protected:virtual void addPeriodTask() {printf("Task1.addPeriodTask()...\n");}virtual void delPeriodTask() {printf("Task1.delPeriodTask()...\n");}virtual void runPeriodTask() {printf("Task1.runPeriodTask()...\n");}
};class Task2 : public PeriodTask { protected:virtual void addPeriodTask() {printf("Task2.addPeriodTask()...\n");}virtual void delPeriodTask() {printf("Task2.delPeriodTask()...\n");}virtual void runPeriodTask() {printf("Task2.runPeriodTask()...\n");}
};class Task3 : public PeriodTask { private:std::future<bool> future;bool futureExec(){// getDownload() 取出一条下载链接printf("futureExec begin...\n");sleep(3); // 模拟下载耗时动作printf("futureExec done...\n");}bool getDownload(std::string &url){// 从队列中取出一条下载链接return true;}size_t getDownloadSize(){return 10; // 返回下载队列数量}protected:virtual void addPeriodTask() {printf("Task3.addPeriodTask()...\n");}virtual void delPeriodTask() {printf("Task3.delPeriodTask() begin...\n");if(future.valid() == true){printf("wait async exit ing...");future.get();}printf("Task3.delPeriodTask() done...\n");}virtual void runPeriodTask() {printf("Task3.runPeriodTask() ...\n");if(future.valid() == true){if(future.wait_for(std::chrono::milliseconds(1)) != std::future_status::ready){printf("Task3.wait_for() ...\n");return ;}future.get();}// 查看是否有需要下载的工作if(getDownloadSize() > 0){printf("Task3.runPeriodTask() create async ...\n");future = std::async(std::launch::async, std::bind(&Task3::futureExec, this));}}public:bool addDownload(const char *url){// 将下载链接加入队列中return true;}
};int main()
{std::shared_ptr<MarkTime> sp_marktime;std::shared_ptr<PeriodTasks> sp_tasks;std::shared_ptr<Task1> sp_task1;std::shared_ptr<Task2> sp_task2;std::shared_ptr<Task3> sp_task3;sp_marktime = std::make_shared<MarkTime>();sp_tasks = std::make_shared<PeriodTasks>();sp_task1 = std::make_shared<Task1>();sp_task2 = std::make_shared<Task2>();sp_task3 = std::make_shared<Task3>();sp_tasks->addTask(sp_task1, 1000);sp_tasks->addTask(sp_task2, 2000);sp_tasks->addTask(sp_task3, 2000);sp_tasks->addTask(sp_task3, 5000); // 重复添加会失败// 可以在其它线程调用sp_task3->addDownload("https://.....");sp_tasks->modTask(sp_task3, 500);sp_marktime->mark();while(sp_marktime->getMillisecond() < 20 * 1000){if(sp_marktime->getMillisecond() > 10 * 1000){sp_tasks->delTask(sp_task3); // 可以在其它线程调用}sp_tasks->runTasks();}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/689466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BES 平台 SDK之串口指令

本文章是基于BES2700 芯片&#xff0c;其他BESxxx 芯片可做参考&#xff0c;如有不当之处&#xff0c;欢迎评论区留言指出。仅供参考学习用&#xff01; 上位机下发格式&#xff1a; 格式一&#xff1a;[A,B] 格式二&#xff1a;A:B 固定格式&#xff1a;auto_test&#xff1a…

政安晨:【完全零基础】认知人工智能(二)【超级简单】的【机器学习神经网络】—— 底层算法

如果小伙伴第一次看到这篇文章&#xff0c;可以先浏览一下我这个系列的上一篇文章&#xff1a; 政安晨&#xff1a;【完全零基础】认知人工智能&#xff08;一&#xff09;【超级简单】的【机器学习神经网络】 —— 预测机https://blog.csdn.net/snowdenkeke/article/details/…

阿里云服务器CPU内存配置怎么选择够用?

阿里云服务器配置怎么选择&#xff1f;根据实际使用场景选择&#xff0c;个人搭建网站可选2核2G配置&#xff0c;访问量大的话可以选择2核4G配置&#xff0c;企业部署Java、Python等开发环境可以选择2核8G配置&#xff0c;企业数据库、Web应用或APP可以选择4核8G配置或4核16G配…

技能下载中:Sora视频让机器人秒学任何技艺!

引言 在机器人成为平凡工匠和前沿先驱的时代&#xff0c;我们正站在新黎明的边缘。本文将探讨斯坦福大学的通用操作接口&#xff08;UMI&#xff09;及其与OpenAI的Sora如何共同推进机器人技术&#xff0c;开创未来学习的新纪元。 正文 斯坦福的通用操作接口&#xff08;UMI…

WildCard:一个因太好用而被迫暂停服务的虚拟信用卡平台,魅力何在?

如果你需要使用Wildcard开通GPT4、Midjourney或是Only方式的话&#xff0c;请点击&#xff1a;WildCard使用教程 参考文章链接&#xff1a;WildCard&#xff1a;一个因太好用而被迫暂停服务的虚拟信用卡平台&#xff0c;魅力何在&#xff1f; 1、Wildcard用户数量激增&#x…

Vite之对CSS的处理方式及使用

Vite之对CSS的处理方式及使用 文章目录 Vite之对CSS的处理方式及使用1. 直接对CSS处理2. 使用css预处理器less 1. 直接对CSS处理 可直接导入css进行使用&#xff0c;而不需要任何配置 定义样式文件&#xff1a;myStyle.css body {background-clor:red }导入css文件 import …

Windows 编译 yangfengzzz/fluid-engine-OpenVDB

我想将 OpenVDB 接入 doyubkim 的流体引擎 https://github.com/doyubkim/fluid-engine-dev 然后搜到已经有人做过这件事了 https://github.com/yangfengzzz/fluid-engine-OpenVDB Windows 编译 yangfengzzz/fluid-engine-OpenVDB 但是我是 windows&#xff0c;所以想要编译…

ubuntu22.04-cmake-添加动态库-静态库-添加头文件夹所在位置-管理员启动

文章目录 1.引用库命令-target_link_libraries2.命令行添加头文件夹和库文件夹所在位置3.添加头文件目录-include_directories4.target_include_directories5.生成库-add_library6.静态库-共享库7.管理员任务管理器1.引用库命令-target_link_libraries 作用 指定链接给定目标和…

MySQL系列之索引入门(下)

前言 通过上文&#xff0c;我想各位盆友已熟悉MySQL的索引分类及其含义&#xff0c;那么如何合理的使用呢&#xff1f; 请继续围观此文&#xff0c;一探究竟&#xff01; 一、创建索引 首先&#xff0c;我们一起学习索引是如何创建的&#xff0c;又有哪些方式。 1. create t…

VO、DTO、DO、BO、PO

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 VO、DTO、DO、BO1.概念阿里Java开发手册分层领域模型&#xff1a; 2. VO 和 DTO 使用场景以下是一个使用VO和DTO的典型案例&#xff1a; 3.BO和DTO的区别 案例 VO、…

259.【华为OD机试真题】特殊的加密算法(深度优先搜索(DFS)-JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-特殊的加密算法二.解题思路三.题解代码Python题…

js-后端返回参数前端动态切换组件样式

1.js枚举 // 枚举-js //对应的icon显示 EnumUtil.Type { COMMON: 1, BUY: 2, PRODUCE: 3} 2.动态公共样式组件 <!-- 公共组件-显示对应icon --> <template><div v-html"TaskIcon(statues)" class"pro_set_task"></div> </t…

数据结构1.0(基础)

近java的介绍&#xff0c; 文章目录 第一章、数据结构1、数据结构 &#xff1f;2、常用的数据结构数据结构&#xff1f; 逻辑结构and物理结构 第二章、数据结构基本介绍2.1、数组&#xff08;Array&#xff09;2.2、堆栈&#xff08;Stack&#xff09;2.3、队列&#xff08;Que…

GET 和 POST 的区别?

get 参数通过 url 传递&#xff0c;post 放在 request body 中。 get 请求在 url 中传递的参数是有长度限制的&#xff0c;而 post 没有。 get 比 post 更不安全&#xff0c;因为参数直接暴露在 url 中&#xff0c;所以不能用来传递敏感信息。 get 请求只能进行 url 编码&am…

宝塔安装MySQL、设置MySQL密码、设置navicat连接

1、登录宝塔面板进行安装 2、设置MySQL连接密码 3、安装好了设置navicat连接 登录MySQL [roothecs-394544 ~]# mysql -uroot -p Enter password: 切换到MySQL数据 mysql> use mysql Database changed mysql> 查询用户信息 mysql> select host,user from user; ---…

数据脱敏(四)脱敏算法-替换算法

脱敏算法篇使用阿里云数据脱敏算法为模板,使用算子平台快速搭建流程来展示数据 "替换脱敏"是一种数据处理技术&#xff0c;主要用于保护个人隐私和数据安全。它通过将敏感信息&#xff08;如姓名、身份证号、电话号码等&#xff09;替换为无意义或随机的字符&#xf…

蓝桥杯-整数删除

给定一个长度为 N 的整数数列&#xff1a;A1, A2, ... , AN。你要重复以下操作 K 次&#xff1a; 每次选择数列中最小的整数&#xff08;如果最小值不止一个&#xff0c;选择最靠前的&#xff09;&#xff0c;将其删除。 并把与它相邻的整数加上被删除的数值。 输出 K 次操作后…

完整的 vue-router 导航解析流程

在Vue.js中&#xff0c;vue-router是一个官方提供的路由管理器&#xff0c;它能够帮助我们实现页面之间的无缝切换和导航。 本文将深入探讨vue-router的导航解析流程&#xff0c;并通过示例代码演示如何使用vue-router实现完整的导航过程。 首先&#xff0c;让我们来了解一下…

Linux环境安装Maven(详细图文)

目录 摘要 一、准备工作 1.检查当前环境是否安装maven 2.下载maven ​3.上传maven压缩包 4.解压maven包 5.移动到/usr/local目录下方便管理 6.配置maven环境变量 7.刷新配置文件 8.配置maven镜像仓库 9.验证是否成功 摘要 笔者Linux环境为&#xff1a;Ubuntu 22.04 …

OpenAI 官方论坛

OpenAI 论坛是一项新举措,汇集了领域专家和学生,就人工智能的现在和未来进行讨论和合作。该论坛举办的活动包括重点技术讲座的面对面聚会、OpenAI 的晚宴、教育网络研讨会和专家圆桌对话,以及为成员(包括 OpenAI 研究人员)提供大量交流和交流想法的机会。论坛节目将由社区…