【BES2500x系列 -- RTX5操作系统】深入探索CMSIS-RTOS RTX -- 同步与通信篇 -- 消息队列和邮箱处理 --(四)

请添加图片描述

  • 💌 所属专栏:【BES2500x系列】

  • 😀 作  者:我是夜阑的狗🐶

  • 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询!

  • 💖 欢迎大家:这里是CSDN,我总结知识的地方,喜欢的话请三连,有问题请私信 😘 😘 😘

您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!🤩 🤩 🤩

请添加图片描述

文章目录

  • 前言
  • 1 介绍
  • 2 功能特性
  • 3 同步与通信
      • 3.1 通信
  • 4 同步与通信
      • 4.1 消息队列
        • 4.1.1 定义
        • 4.1.2 创建
        • 4.1.3发送消息
      • 4.2 邮箱处理
        • 4.2.1 定义
        • 4.2.2 创建
        • 4.2.3 发送/释放邮件
        • 4.2.4 获取邮件
  • 总结


前言

  大家好,又见面了,我是夜阑的狗🐶,本文是专栏【BES2500x系列】专栏的第4篇文章;
  今天开始学习BES2500x系列的一天💖💖💖,开启新的征程,记录最美好的时刻🎉,每天进步一点点。
  专栏地址:【BES2500x系列】, 此专栏是我是夜阑的狗对BES2500x系列开发过程的总结,希望能够加深自己的印象,以及帮助到其他的小伙伴😉😉。
  如果文章有什么需要改进的地方还请大佬不吝赐教👏👏。


<<【系列文章索引】>>

1 介绍

  在嵌入式系统中,同步和通信是确保系统内各个组件协调工作的两个核心概念。它们对于实现高效、可靠的嵌入式应用至关重要。前面已经讲过同步概念了,接下来对通信概念进行简要说明。话不多说,那接下来就学习 RTX 系统中通信机制都有哪些吧,让我们原文再续,书接上回吧。😉

在这里插入图片描述

2 功能特性

  在实时操作系统(RTOS)中,任务管理和同步通信是关键组件,它们确保系统的高效和有序执行。本文将探讨这些概念,特别是线程管理、信号量、互斥锁、消息队列和邮箱处理。

  • 任务管理:RTX提供任务创建、调度和优先级管理,确保任务按照优先级及时执行。
  • 同步与通信:包括信号量、互斥锁、消息队列和邮箱,促进任务间的同步和数据交换。
  • 内存管理:内存池和动态内存分配,有效管理有限的系统资源。
  • 定时器服务:虚拟和硬件定时器,支持周期性任务和一次性事件触发。
  • 中断处理:保证中断服务的快速响应,同时保持任务的上下文安全。
  • 线程安全:通过内核级保护机制,防止多线程环境下的数据竞争和死锁。

3 同步与通信

3.1 通信

  通信是指嵌入式系统中不同组件或任务之间交换信息的过程。有效的通信机制对于分布式系统和多处理器系统尤为重要。嵌入式系统中常用的通信方式包括:

序号方法说明
1消息队列(Message Queues)任务间通过发送和接收带有数据的消息来通信,支持异步通信。
2管道(Pipes)一种半双工的数据传输方式,常用于进程间的通信。
3共享内存(Shared Memory)多个任务可以直接读写同一块内存区域,效率高但需要同步机制来避免冲突。
4总线(Buses)I2CSPIUART等硬件接口,用于设备间的数据传输。
5远程过程调用(RPC)允许程序调用网络中另一台计算机上的子程序,模拟本地调用。
6中断(Interrupts)硬件触发的事件,用于通知 CPU 处理紧急或外部事件,是一种快速的通信方式。

  同步和通信机制的选择取决于嵌入式系统的具体需求,包括实时性、资源限制、复杂度以及系统的可靠性要求。合理设计同步和通信策略,是保证嵌入式系统高效稳定运行的关键。

4 同步与通信

4.1 消息队列

  消息队列允许线程间安全地传递固定大小的消息,提供了异步通信的方式。

4.1.1 定义

  消息队列允许线程安全地发送和接收固定大小的数据块。队列维护发送和接收的顺序。一般在文件开头会看到这样的定义:osMessageQDef

  • 代码
// 定义一个名为app_test1_queue的消息队列,可存储128个uint32_t类型的元素。
osMessageQDef(app_test1_queue, 128, uint32_t);// 声明一个osMessageQId类型的变量app_test1_queue_id,用于保存消息队列的句柄。
// 在程序运行时,需要通过调用相关API初始化并获取有效的句柄值。
osMessageQId app_test1_queue_id = NULL;

  这段代码首先使用 osMessageQDef 宏定义了一个名为 app_test1_queue 的消息队列,它可以存储128个32位无符号整数。然后声明了一个变量 app_test1_queue_id ,用于存储消息队列的标识符(句柄),初始值设为 NULL 。在实际应用中,需要通过操作系统提供的API来初始化这个消息队列,并将返回的句柄赋值给app_test1_queue_id

/*** 定义一个消息队列。* * 这个宏用于静态定义一个消息队列,它会创建一个静态队列控制块和一个用于存储消息的数据缓冲区。* * @param name 消息队列的名称。* @param queue_sz 队列中能容纳的消息数量。* @param type 消息队列中每条消息的数据类型。*/
#define osMessageQDef(name, queue_sz, type) \
static StaticQueue_t os_mq_cb_##name; \
static uint32_t os_mq_data_##name[(queue_sz) * sizeof(type)]; \
const osMessageQDef_t os_messageQ_def_##name = \
{ (queue_sz), \{ NULL, 0U, (&os_mq_cb_##name), sizeof(StaticQueue_t), \(&os_mq_data_##name), sizeof(os_mq_data_##name) } }

  此宏定义了三个静态变量:一个静态队列控制块,一个消息数据数组,和一个用于OS的消息队列定义结构体。这个结构体包含了队列的大小、指针到静态队列控制块和消息数据数组的地址,以及这些数组的大小。这使得在系统运行时能够直接使用这个消息队列而无需动态分配内存。

  • 参数/函数讲解
序号参数/函数说明
1osMessageQId声明变量,用于存储消息队列的标识符(句柄),初始值设为NULL。
2osMessageQDef定义了静态变量:静态队列控制块,消息数据数组和用于OS的消息队列定义结构体
4.1.2 创建

  通过 osMessageQueueCreate() 函数创建消息队列,指定队列容量和消息大小。

  • 代码
/*** 初始化app_test1_queue消息队列。** 这个函数负责创建名为app_test1_queue的消息队列,并将成功创建的句柄保存到全局变量app_test1_queue_id。* 如果消息队列创建失败,它会记录错误信息并返回-1。** @return*   - 0: 消息队列创建成功。*   - -1: 创建消息队列失败。*/
static int32_t app_test1_queue_init(void)
{// 使用osMessageCreate函数创建消息队列,并将句柄保存到全局变量app_test1_queue_id = osMessageCreate(osMessageQ(app_test1_queue), NULL);// 检查创建是否成功,如果失败则打印错误信息并返回-1if (app_test1_queue_id == NULL) {TRACE(0, "Failed to Create app_test_thread1_queue");return -1;}// 创建成功,返回0return 0;
}

  这段代码定义了一个名为 app_test1_queue_init 的静态函数,用于初始化之前定义的消息队列app_test1_queue 。它通过调用o sMessageCreate 函数创建消息队列,并检查返回的句柄是否有效。如果创建失败,它会记录错误信息并返回 -1 ;否则,返回 0 表示成功。

  • 参数/函数讲解
序号参数/函数说明
1osMessageCreate创建消息队列
4.1.3发送消息

  使用 osMessageQueueSend()osMessageQueuePut() 函数向队列发送消息。

  • 代码
/*** 尝试向app_test1_queue中发送消息。** 此函数检查消息队列是否有足够的空间接收至少6条消息。如果队列有足够空间,* 它将向队列中放入一个值为0xFF的消息,不设置优先级。** 注意:这个函数没有处理消息队列满的情况,因此在队列满时不会阻塞。*/
void app_test1_queue_put(void)
{// 检查消息队列是否有超过5个空闲槽位if (osMessageGetSpace(app_test1_queue_id) > 5) {// 向消息队列app_test1_queue_id中插入一个值为0xFF的消息,优先级设为0osMessagePut(app_test1_queue_id, 0xFF, 0);}
}

  这个函数 app_test1_queue_put 尝试将一个值为 0xFF 的消息放入名为 app_test1_queue 的消息队列中。首先,它检查队列是否有足够的空间容纳至少6个新消息。如果满足条件,就调用 osMessagePut 将消息放入队列,否则不做任何操作。注意,这个函数没有处理队列已满的情况,所以如果队列已满,消息将不会被发送,也不会阻塞调用线程。

  • 参数/函数讲解
序号参数/函数说明
1osMessageGetSpace检查消息队列的空闲槽位
2osMessagePut将消息放入队列

4.2 邮箱处理

  邮箱是用于线程间交换结构化数据的对象池。每个邮箱包含一组预先分配的内存块,线程可以申请、发送和接收这些内存块。

4.2.1 定义

  一般在文件开头会看到这样的定义:osMailQDef

  • 代码
osMailQDef (app_test1_mailbox, APP_TEST1_MAX_MAILBOX, APP_TEST1_MAIL); 
/*** app_test1_mailbox: 邮箱队列定义** 使用osMailQDef宏定义一个名为'app_test1_mailbox'的邮箱队列,最大邮件数为APP_TEST1_MAX_MAILBOX,* 邮件类型为APP_TEST1_MAIL。*/// 邮箱队列ID,用于后续操作
static osMailQId app_test1_mailbox_id = NULL;/*** app_test1_mail_alloc - 分配并初始化一个APP_TEST1_MAIL类型的邮件** @param mail 指向邮件指针的指针,用于存放新分配的邮件地址。** 返回值: 成功分配时返回0,失败则返回非0值。** 此函数为内部使用,负责从'app_test1_mailbox'邮箱队列中分配一个新的邮件,并将其地址存储在* 输入参数'mail'指向的变量中。具体实现省略。*/
static int app_test1_mail_alloc(APP_TEST1_MAIL** mail)
{// ...
}

  osMailQDef 定义了一个名为 app_test1_mailbox 的邮箱队列,用于存储 APP_TEST1_MAIL 类型的数据。APP_TEST1_MAX_MAILBOX 定义了邮箱队列可容纳的最大邮件数量。这个邮箱队列可以用于多线程或任务之间的数据通信,确保数据安全地传递。

/*** 定义一个邮箱队列。* * 该宏用于静态定义一个邮箱队列以及相关的OS邮箱队列结构体。它为指定的邮箱队列分配内存,* 并初始化OS邮箱队列结构体。* * @param name 邮箱队列的名称。* @param queue_sz 邮箱队列中邮件的最大数量。* @param type 邮件中元素的类型。*/
#define osMailQDef(name, queue_sz, type) \
static uint32_t os_mailQ_m_##name[3+((sizeof(type)+3)/4)*(queue_sz)]; \
osMailQDef_t os_mailQ_def_##name = \
{ {(queue_sz), sizeof(type), (os_mailQ_m_##name)}, NULL, {NULL} }

  在上述宏定义中:

  (1) 第一部分定义了一个静态数组 os_mailQ_m_##name,用于存储邮箱队列中的邮件。数组大小根据邮件类型 type 的大小和队列大小 queue_sz 动态计算得出。
  (2) 第二部分定义了一个 osMailQDef_t 类型的结构体 os_mailQ_def_##name,其中包含了邮箱队列的配置信息,如队列大小、邮件类型大小以及邮件存储区的指针。

  • 参数/函数讲解
序号参数/函数说明
1osMailQDef定义了的邮箱队列,用于存储 APP_TEST1_MAIL 类型的数据
2app_test1_mailbox_id是一个全局变量,用于存储邮箱队列的标识符,方便后续操作
3app_test1_mail_alloc用于从 app_test1_mailbox 中分配一个新的邮件,并将分配的邮件地址通过参数 mail 返回
4os_mailQ_m_##name用于存储邮箱队列中的邮件
5osMailQDef_t定义结构体,其中包含了邮箱队列的配置信息
4.2.2 创建

  通过 osMailQCreate() 函数创建邮箱,指定邮箱的大小和数据类型。

  • 代码
/*** app_test1_mailbox_init - 初始化app_test1_mailbox邮箱队列** @return: 成功初始化时返回0,失败则返回-1。** 此函数用于初始化之前定义的'app_test1_mailbox'邮箱队列。它调用osMailCreate函数创建邮箱队列,* 并将返回的邮箱ID存储在全局变量'app_test1_mailbox_id'中。如果创建失败,函数会输出错误信息* "Failed to Create app_test_thread1_mailbox",并返回-1表示初始化失败。*/
static int32_t app_test1_mailbox_init(void)
{app_test1_mailbox_id = osMailCreate(osMailQ(app_test1_mailbox), NULL);if (app_test1_mailbox_id == NULL) {TRACE(0, "Failed to Create app_test_thread1_mailbox");return -1;}return 0;
}

  这个函数 app_test1_mailbox_init 负责初始化之前通过 osMailQDef 宏定义的 app_test1_mailbox 邮箱队列。如果初始化成功,它将返回0;如果失败(即无法创建邮箱队列),它会打印错误信息并返回-1。

  • 参数/函数讲解
序号参数/函数说明
1osMailCreate创建邮箱队列
4.2.3 发送/释放邮件

  使用 osMailQAlloc() 分配邮箱中的空间,然后用 osMailPut() 发送邮件。

  • 代码
/*** app_test1_mail_send - 发送一个APP_TEST1_MAIL类型的邮件到app_test1_mailbox** @param mail 需要发送的邮件对象指针。** 返回值: 成功发送时返回0,失败则返回非0值。** 此函数用于将一个APP_TEST1_MAIL类型的邮件对象发送到'app_test1_mailbox'邮箱队列中。* 具体实现省略,可能涉及到邮箱队列的同步原语以保证线程安全。*/
static int app_test1_mail_send(APP_TEST1_MAIL* mail)
{// ...
}/*** app_test1_mail_free - 释放app_test1_mailbox中的一个邮件对象** @param mail_p 已分配的邮件对象指针。** 返回值: 成功释放时返回0,失败则返回非0值。** 此函数用于释放'app_test1_mailbox'邮箱队列中已分配的一个邮件对象,以便于后续再使用。* 具体实现省略,可能涉及邮箱队列的同步原语以保证线程安全。*/
static int app_test1_mail_free(APP_TEST1_MAIL* mail_p)
{// ...
}
  • 参数/函数讲解
序号参数/函数说明
1app_test1_mail_send用于向 app_test1_mailbox 邮箱队列中发送邮件
2app_test1_mail_free用于向 app_test1_mailbox 邮箱队列中释放已分配的邮件
4.2.4 获取邮件

  线程通过 osMailGet() 函数获取邮件,可以选择等待或立即返回。

  • 代码
/*** @brief          获取应用测试1的邮件对象* * @description    该函数从内部数据结构中获取一个`APP_TEST1_MAIL`类型的邮件对象。*                 如果邮件可用,它将分配内存并填充邮件内容,然后将其指针返回。* * @param[out]     mail_p     指向接收`APP_TEST1_MAIL`结构体指针的指针。*                            如果成功获取邮件,此参数将被设置为有效邮件对象的指针。* * @return         成功获取邮件对象返回0,否则返回非0错误代码:*                 -1:邮件队列为空*                 -2:内存分配失败*                 其他值:可能表示其他错误情况** @note           实现应考虑线程安全,可能需要加锁来保护数据结构。*                 如果队列为空,可以选择阻塞等待,直到有新邮件到达。*/
static int app_test1_mail_get(APP_TEST1_MAIL** mail_p)
{// 实现获取邮件对象的逻辑,包括检查队列、分配内存、填充邮件内容等// ...if (/* 邮件队列为空或分配内存失败等错误条件 */) {return -1; // 或者 -2}return 0; // 成功获取邮件
}

  app_test1_mail_get 函数用于从 app_test1_mailbox 邮箱队列中取出一个邮件对象。当邮箱队列为空时,函数可能阻塞等待,直到有新的邮件可供消费。函数返回0表示成功获取邮件,非0值表示队列为空或出现错误。具体实现细节被省略,实际操作中可能需要考虑线程同步问题。

  • 参数/函数讲解
序号参数/函数说明
1app_test1_mail_get用于从邮箱队列中取出一个邮件对象

<<【系列文章索引】>>

请添加图片描述


总结

  感谢观看,这里就是 同步与通信篇 – 消息队列和邮箱处理,如果觉得有帮助,请给文章点个赞吧,让更多的人看到。🌹 🌹 🌹

在这里插入图片描述

  也欢迎你,关注我。👍 👍 👍

  原创不易,还希望各位大佬支持一下,你们的点赞、收藏和留言对我真的很重要!!!💕 💕 💕 最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!下期再见。🎉

更多专栏订阅:

  • 😀 【LeetCode题解(持续更新中)】

  • 🥇 【恒玄BES】

  • 🌼 【鸿蒙系统】

  • 💎 【蓝牙协议栈】

  • 🎃 【死机分析】

  • 👑 【Python脚本笔记】

  • 🚝 【Java Web项目构建过程】

  • 💛 【微信小程序开发教程】

  • 【JavaScript随手笔记】

  • 🤩 【大数据学习笔记(华为云)】

  • 🦄 【程序错误解决方法(建议收藏)】

  • 🔐 【Git 学习笔记】

  • 🚀 【软件安装教程】



订阅更多,你们将会看到更多的优质内容!!

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

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

相关文章

经典FC游戏web模拟器--EmulatorJS

简介 EmulatorJS是一个基于JavaScript和Webassembly技术的虚拟环境的实现&#xff0c;可以在网页中运行各种经典FC游戏系统&#xff0c;支持任天堂、世嘉、雅达利等经典红白机。EmulatorJS的诞生使得诸如超级玛丽、坦克大战、魂斗罗等经典FC游戏能够以一种全新的方式回归。本文…

SAP MM模块的ATP检查

前面几篇文章都演示和说明ATP的一些设置和操作&#xff0c;通常情况下ATP的检查PP模块&#xff0c;SD模块用的相对来说是比较多的&#xff0c;但是实际上MM模块也会遵循ATP的可用性的检查规则。 当我们在做311、301等移动类型时&#xff0c;系统会根据相应的可用性检查规则&am…

Linux常用指令汇总

Linux常用指令汇总 Cfilt 功能&#xff1a;解析C程序中被修饰的符号&#xff0c;比如变量与函数名称。 示例&#xff1a; 解析编译器 g 修饰的函数名称。 cfilt -s gnu-v3 _Z5printRKSs print(std::basic_string<char, std::char_traits<char>, std::allocator<…

Django 多对多关系

多对多关系作用 Django 中&#xff0c;多对多关系模型的作用主要是为了表示两个模型之间的多对多关系。具体来说&#xff0c;多对多关系允许一个模型的实例与另一个模型的多个实例相关联&#xff0c;反之亦然。这在很多实际应用场景中非常有用&#xff0c;比如&#xff1a; 博…

【每日一个Git命令: cherry-pick】

git cherry-pick 命令的作用是将指定的提交&#xff08;commit&#xff09;应用到其他分支上。这个命令允许你选择一个或多个已有的提交&#xff0c;并将它们作为新的提交引入到当前分支中。 这个过程不会改变项目的历史记录&#xff0c;因为它实际上是创建了这些提交的副本。…

BMA530 运动传感器

型号简介 BMA530是博世&#xff08;bosch-sensortec&#xff09;的一款运动传感器。时尚简约的可穿戴设备为功能强大的组件提供了很小的空间。具有先进功能集的下一代加速度计是世界上最小的加速度传感器&#xff08;1.2 x 0.8 x 0.55 mm&#xff09;。它专为紧凑型设备而设计&…

24/07/02数据结构(1.1201)算法效率顺序表

数据结构基本内容:1.时间复杂度 空间复杂度2.顺序表链表3.栈 队列4.二叉树5.排序 数据结构是存储,组织数据的方式.指相互之间存在一种或多种特定关系的数据元素的集合 算法是定义良好的计算过程.取一个或一组值为输入并产生一个或一组值为输出. 需要知道虽然选择题有20-30个…

Leetcode1114 交替打印 FooBar及其测试

题目描述 相关标签 相关企业 给你一个类&#xff1a; class FooBar { public void foo() { for (int i 0; i < n; i) { print(“foo”); } } public void bar() { for (int i 0; i < n; i) { print(“bar”); } } } 两个不同的线程将会共用一个 FooBar 实例&#xf…

python自动化运维--DNS处理模块dnspython

1.dnspython介绍 dnspython是Pyhton实现的一个DNS工具包&#xff0c;他几乎支持所有的记录类型&#xff0c;可以用于查询、传输并动态更新ZONE信息&#xff0c;同事支持TSIG&#xff08;事物签名&#xff09;验证消息和EDNS0&#xff08;扩展DNS&#xff09;。在系统管理方面&a…

Linux高并发服务器开发(九)Tcp状态转移和IO多路复用

文章目录 0 包裹函数1 多进程服务器流程代码 2 多线程服务器3 TCP状态转移半关闭心跳包 4 端口复用5 IO多路复用技术高并发服务器 6 select代码总结 7 POLLAPI代码poll相对select的优缺点 8 epoll&#xff08;重点&#xff09;API监听管道代码EPOLL 高并发服务器 9 Epoll的两种…

Iot解决方案开发的体系结构模式和技术

前言 Foreword 计算机技术起源于20世纪40年代&#xff0c;最初专注于数学问题的基本原理&#xff1b;到了60年代和70年代&#xff0c;它以符号系统为中心&#xff0c;该领域首先开始面临复杂性问题&#xff1b;到80年代&#xff0c;随着个人计算的兴起和人机交互的问题&#x…

【进阶篇】Java 项目中对使用递归的理解分享

前言 笔者在最近的项目开发中&#xff0c;遇到了两个父子关系紧密相关的场景&#xff1a;评论树结构、部门树结构。具体的需求如&#xff1a;找出某条评论下的所有子评论id集合&#xff0c;找出某个部门下所有的子部门id集合。 在之前的项目开发经验中&#xff0c;递归使用得是…

centos7安装python3.10

文章目录 1. 安装依赖项2. 下载Python 3.10源码3. 解压源码并进入目录4. 配置安装选项5. 编译并安装Python6. 验证安装7.创建软连接8. 安装pip39. 换源 1. 安装依赖项 sudo yum groupinstall -y "Development Tools" sudo yum install -y openssl-devel bzip2-devel…

Eureka的自扩展之道:服务自动扩展的秘诀

&#x1f31f; Eureka的自扩展之道&#xff1a;服务自动扩展的秘诀 在微服务架构中&#xff0c;服务的自动扩展是实现高可用性和弹性伸缩的关键。Eureka作为Netflix开源的服务发现框架&#xff0c;提供了一套机制来支持服务的自动扩展。本文将详细介绍Eureka如何实现服务的自动…

【LeetCode】十、二分查找法:寻找峰值 + 二维矩阵的搜索

文章目录 1、二分查找法 Binary Search2、leetcode704&#xff1a;二分查找3、leetcode35&#xff1a;搜索插入位置4、leetcode162&#xff1a;寻找峰值5、leetcode74&#xff1a;搜索二维矩阵 1、二分查找法 Binary Search 找一个数&#xff0c;有序的情况下&#xff0c;直接…

第4章:Electron主窗口与子窗口管理

4.1 创建主窗口 主窗口是 Electron 应用启动后显示的第一个窗口&#xff0c;通常用来承载应用的主界面。我们使用 BrowserWindow 类来创建主窗口。 4.1.1 创建主窗口的基础代码 // 引入 Electron 模块和 Node.js 的 path 模块 const { app, BrowserWindow } require(electr…

【动态规划 前缀和】2478. 完美分割的方案数

本文涉及知识点 划分型dp 动态规划汇总 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 LeetCode 2478. 完美分割的方案数 给你一个字符串 s &#xff0c;每个字符是数字 ‘1’ 到 ‘9’ &#xff0c;再给你两个整数 k 和 minLength 。 如…

【C++ Primer Plus学习记录】指针和const

可以用两种不同的方式将const关键字用于指针。第一种方法是让指针指向一个常量对象&#xff0c;这样就可以防止使用该指针来修改所指向的值&#xff0c;第二种方法是将指针本身声明为常量&#xff0c;这样可以防止改变指针指向的位置。 首先&#xff0c;声明一个指向常量的指针…

前后端防重复提交(续)

前文介绍过前后端防重复提交的基本场景&#xff0c;简单的情况是只发起一个异步请求&#xff0c;如果有多个异步请求怎么操作呢&#xff1f;这个要分情况看下。 如果是后端服务器的接口支持一次传递多个申请&#xff0c;那么可以将任务放进数组中&#xff0c;发往后端。这是最好…

074、Python 关于实例方法、静态方法和类方法

在Python中&#xff0c;类可以定义三种类型的方法&#xff1a;实例方法、静态方法和类方法。每种方法都有其特定的用途和调用方式。 实例方法&#xff08;Instance Methods&#xff09; 定义&#xff1a;实例方法是绑定到类实例上的方法。它们必须有一个名为self的隐式第一个参…