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

Rt-Thread 移植6--多线程(KF32)

6.1 就绪列表

6.1.1 线程就绪优先级组

线程优先级表的索引对应的线程的优先级。
在这里插入图片描述
为了快速的找到线程在线程优先级表的插入和移出的位置,RT-Thread专门设计了一个线程就绪优先级组。线程就绪优先组是一个32位的整型数,每一个位对应一个优先级,最多表示32个优先级

rt_uint32_t  rt_thread_ready_priority_group;

在这里插入图片描述

6.1.2 寻找优先级最高的线程

在这里插入图片描述
在这里插入图片描述

从图中看出,第一个置1的位是位1,即表示此时就绪的线程当中,优先级最高的线程1,然后调度器从线程优先级表的索引1下去除线程1的线程控制块,从而切换到线程1.
kservice.c中

const rt_uint8_t __lowest_bit_bitmap[] =
{/* 00 */ 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* 10 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* 20 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* 30 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* 40 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* 50 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* 60 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* 70 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* 80 */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* 90 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* A0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* B0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* C0 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* D0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* E0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,/* F0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};/*** This function finds the first bit set (beginning with the least significant bit)* in value and return the index of that bit.** Bits are numbered starting at 1 (the least significant bit).  A return value of* zero from any of these functions means that the argument was zero.** @return return the index of the first bit set. If value is 0, then this function* shall return 0.*/
int __rt_ffs(int value)
{if (value == 0) return 0;if (value & 0xff)return __lowest_bit_bitmap[value & 0xff] + 1;if (value & 0xff00)return __lowest_bit_bitmap[(value & 0xff00) >> 8] + 9;if (value & 0xff0000)return __lowest_bit_bitmap[(value & 0xff0000) >> 16] + 17;return __lowest_bit_bitmap[(value & 0xff000000) >> 24] + 25;
}

6.1.3 线程优先级表

scheduler.c

rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];

在这里插入图片描述
将线程插入到线程优先级表和移出分别由scheduler.c的rt_schedule_insert_thread()和rt_schedule_remove_thread()这两个函数实现。

void rt_schedule_insert_thread(struct rt_thread *thread)
{register rt_base_t temp;/* disable interrupt */temp = rt_hw_interrupt_disable();/* change stat */thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK);/* insert thread to ready list */rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),&(thread->tlist));/* set priority mask */#if RT_THREAD_PRIORITY_MAX > 32rt_thread_ready_table[thread->number] |= thread->high_mask;
#endifrt_thread_ready_priority_group |= thread->number_mask;P_DBG("insert thread %x grout %x\n",thread,rt_thread_ready_priority_group);/* enable interrupt */rt_hw_interrupt_enable(temp);
}/** This function will remove a thread from system ready queue.** @param thread the thread to be removed** @note Please do not invoke this function in user application.*/
void rt_schedule_remove_thread(struct rt_thread *thread)
{register rt_base_t temp;/* disable interrupt */temp = rt_hw_interrupt_disable();/* remove thread from ready list */rt_list_remove(&(thread->tlist));if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority]))){rt_thread_ready_priority_group &= ~thread->number_mask;}/* enable interrupt */rt_hw_interrupt_enable(temp);
}

6.2 修改代码

6.2.1 修改线程控制块

struct rt_thread
{char name[RT_NAME_MAX];rt_uint8_t type;rt_uint8_t flag;rt_list_t list;void        *sp;	          /* 线程栈指针 */void        *entry;	          /* 线程入口地址 */void        *parameter;	      /* 线程形参 */	void        *stack_addr;      /* 线程起始地址 */rt_uint32_t stack_size;       /* 线程栈大小,单位为字节 */rt_list_t   tlist;            /* 线程链表节点 */
#if TEST_USE_TICK_ENABLErt_ubase_t init_tick;rt_ubase_t remaining_tick;
#endif**rt_uint8_t current_priority;rt_uint8_t init_priority;rt_uint32_t number_mask;rt_err_t error;rt_uint8_t stat;**};

rtdef.h中定义错误码

#define RT_EOK                          0               /**< There is no error */
#define RT_ERROR                        1               /**< A generic error happens */
#define RT_ETIMEOUT                     2               /**< Timed out */
#define RT_EFULL                        3               /**< The resource is full */
#define RT_EEMPTY                       4               /**< The resource is empty */
#define RT_ENOMEM                       5               /**< No memory */
#define RT_ENOSYS                       6               /**< No system */
#define RT_EBUSY                        7               /**< Busy */
#define RT_EIO                          8               /**< IO error */
#define RT_EINTR                        9               /**< Interrupted system call */
#define RT_EINVAL                       10              /**< Invalid argument */

定义线程状态

#define RT_THREAD_INIT                  0x00                /**< Initialized status */
#define RT_THREAD_READY                 0x01                /**< Ready status */
#define RT_THREAD_SUSPEND               0x02                /**< Suspend status */
#define RT_THREAD_RUNNING               0x03                /**< Running status */
#define RT_THREAD_BLOCK                 RT_THREAD_SUSPEND   /**< Blocked status */
#define RT_THREAD_CLOSE                 0x04                /**< Closed status */
#define RT_THREAD_STAT_MASK             0x0f

修改rt_system_scheduler_init()

void rt_system_scheduler_init(void)
{register rt_base_t offset;for(offset = 0; offset < RT_THREAD_PRIORITY_MAX;offset++){rt_list_init(&rt_thread_priority_table[offset]);}rt_current_priority = RT_THREAD_PRIORITY_MAX - 1;rt_current_thread = RT_NULL;rt_thread_ready_priority_group = 0;/* 初始化线程休眠列表,当线程创建好没有启动之前会被放入到这个列表 */}

6.2.3 修改线程初始化函数rt_thread_init()

rt_err_t rt_thread_init(struct rt_thread *thread,const char *name,void (*entry)(void *parameter),void             *parameter,void             *stack_start,rt_uint32_t       stack_size,**rt_uint8_t priority**)
{rt_object_init((rt_object_t)thread,RT_Object_Class_Thread,name);rt_list_init(&(thread->tlist));thread->entry = (void *)entry;thread->parameter = parameter;thread->stack_addr = stack_start;thread->stack_size = stack_size;/* 初始化线程栈,并返回线程栈指针 */thread->sp = (void *)rt_hw_stack_init( thread->entry,thread->parameter,(void *)((char *)thread->stack_addr + thread->stack_size - 4) );**thread->init_priority = priority;thread->current_priority = priority;thread->number_mask = 0;thread->error = RT_EOK;thread->stat = RT_THREAD_INIT;**}

6.2.4 添加线程启动函数rt_thread_startup()

rt_err_t rt_thread_startup(rt_thread_t thread)
{thread->current_priority = thread->init_priority;thread->number_mask = 1L << thread->current_priority;thread->stat = RT_THREAD_SUSPEND;rt_thread_resume(thread);if(rt_thread_self() != RT_NULL){rt_schedule();}
}
rt_err_t rt_thread_resume(rt_thread_t thread)
{register rt_base_t temp;if((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_SUSPEND){return -RT_ERROR;}temp = rt_hw_interrupt_disable();rt_list_remove(&(thread->tlist));rt_hw_interrupt_enable(temp);rt_schedule_insert_thread(thread);
}

6.2.5 修改空闲线程初始化函数rt_thread_idle_init()

void rt_thread_idle_init(void)
{rt_thread_init(&idle,"idle",rt_thread_idle_entry,RT_NULL,&rt_thread_stack[0],sizeof(rt_thread_stack),RT_THREAD_PRIORITY_MAX - 1);**rt_thread_startup(&idle);**}

6.2.6 修改启动系统调度器函数rt_system_scheduler_start()

不再是手动指定第一个需要运行的线程,而是根据优先级来决定第一个运行的线程。

void rt_system_scheduler_start(void)
{register struct rt_thread *to_thread;register rt_ubase_t highest_ready_priority;highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group)-1;P_DBG("first start pri %d\n",highest_ready_priority);to_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,struct rt_thread,tlist);rt_current_thread = to_thread;rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);}

6.2.7 修改系统调度函数rt_schedule()

根据优先级来实现

void rt_schedule(void)
{register rt_base_t temp;rt_base_t level;register rt_ubase_t highest_ready_priority;struct rt_thread *to_thread;struct rt_thread *from_thread;temp = rt_hw_interrupt_disable();highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group)-1;P_DBG("read pri %d\n",highest_ready_priority);to_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,struct rt_thread,tlist);P_DBG("read tgread %x\n",to_thread);if(to_thread != rt_current_thread){P_DBG("!= current thread %x\n",rt_current_thread);rt_current_priority = (rt_uint8_t) highest_ready_priority;from_thread = rt_current_thread;rt_current_thread = to_thread;rt_hw_context_switch((rt_uint32_t)&from_thread->sp,(rt_uint32_t)&to_thread->sp);rt_hw_interrupt_enable(temp);}else{rt_hw_interrupt_enable(temp);}/* 产生上下文切换 */}

6.2.8 修改线程阻塞函数


void rt_thread_delay(rt_tick_t tick)
{register rt_base_t temp;struct rt_thread *thread;temp = rt_hw_interrupt_disable(); thread = rt_current_thread;thread->remaining_tick = tick;thread->stat = RT_THREAD_SUSPEND; rt_thread_ready_priority_group &= ~thread->number_mask;rt_hw_interrupt_enable(temp);rt_schedule();
}

6.2.9 修改时基更i新函数rt_tick_increase()

void rt_tick_increase(void)
{rt_ubase_t i;struct rt_thread *thread;rt_tick++;for(i = 0;i < RT_THREAD_PRIORITY_MAX;i++){thread = rt_list_entry(rt_thread_priority_table[i].next,struct rt_thread,tlist);if(thread->remaining_tick > 0){thread->remaining_tick--;if(thread->remaining_tick == 0){rt_thread_ready_priority_group |= thread->number_mask;}}rt_schedule();}
}

6.3 main.c

/* 初始化线程 */rt_thread_init( &rt_flag1_thread,   "flag1",              /* 线程控制块 */flag1_thread_entry,               /* 线程入口地址 */RT_NULL,                          /* 线程形参 */&rt_flag1_thread_stack[0],        /* 线程栈起始地址 */sizeof(rt_flag1_thread_stack),3 );  /* 线程栈大小,单位为字节 *//* 将线程插入到就绪列表 *///rt_list_insert_before( &(rt_thread_priority_table[0]),&(rt_flag1_thread.tlist) );P_DBG("thread flag1 %x timer %s\n",&rt_flag1_thread,rt_flag1_thread.thread_timer.parent.name);rt_thread_startup(&rt_flag1_thread);/* 初始化线程 */rt_thread_init( &rt_flag2_thread,           "falg2",      /* 线程控制块 */flag2_thread_entry,               /* 线程入口地址 */RT_NULL,                          /* 线程形参 */&rt_flag2_thread_stack[0],        /* 线程栈起始地址 */sizeof(rt_flag2_thread_stack),4 );  /* 线程栈大小,单位为字节 *//* 将线程插入到就绪列表 */P_DBG("thread flag1 %x timer %s\n",&rt_flag2_thread,rt_flag2_thread.thread_timer.parent.name);//rt_list_insert_before( &(rt_thread_priority_table[1]),&(rt_flag2_thread.tlist) );rt_thread_startup(&rt_flag2_thread);/* 启动系统调度器 */rt_system_scheduler_start();/* 线程1 */
void flag1_thread_entry( void *p_arg )
{for( ;; ){P_DBG("flag1 thread\n");flag2 = 1;P_DBG("flag1 thread will dela1\n");rt_thread_delay(10);flag2 = 0;P_DBG("flag1 thread will dela2\n");rt_thread_delay(10);}
}/* 线程2 */
void flag2_thread_entry( void *p_arg )
{for( ;; ){P_DBG("flag2 thread\n");flag2 = 1;rt_thread_delay(2);flag2 = 0;P_DBG("flag2 thread will dela1\n");rt_thread_delay(2);}
}

相关文章:

Rt-Thread 移植6--多线程(KF32)

6.1 就绪列表 6.1.1 线程就绪优先级组 线程优先级表的索引对应的线程的优先级。 为了快速的找到线程在线程优先级表的插入和移出的位置&#xff0c;RT-Thread专门设计了一个线程就绪优先级组。线程就绪优先组是一个32位的整型数&#xff0c;每一个位对应一个优先级&#xff…...

HarmonyOS应用开发-首选项与后台通知管理

首选项 在移动互联网蓬勃发展的今天&#xff0c;移动应用给我们生活带来了极大的便利&#xff0c;这些便利的本质在于数据的互联互通。因此在应用的开发中数据存储占据了非常重要的位置&#xff0c;HarmonyOS应用开发也不例外。本章以HarmonyOS的首选项为例&#xff0c;介绍了…...

通过easyexcel导出数据到excel表格

这篇文章简单介绍一下怎么通过easyexcel做数据的导出&#xff0c;使用之前easyui构建的歌曲列表crud应用&#xff0c;添加一个导出按钮&#xff0c;点击的时候直接连接后端接口地址&#xff0c;在后端的接口完成数据的导出功能。 前端页面完整代码 let editingId; let request…...

Android---MVP 中 presenter 声明周期的管理

我们经常在 Android MVP 架构中的 Presenter 层做一些耗时操作&#xff0c;比如请求网络数据&#xff0c;然后根据请求后的结果刷新 View。但是&#xff0c;如果按返回结束 Activity&#xff0c;而 Presenter 依然在执行耗时操作。那么就有可能造成内存泄漏&#xff0c;严重时甚…...

Oracle中的索引碎片

索引碎片是指索引在存储空间上不连续的分布情况&#xff0c;它可能会影响到数据库性能和查询效率。索引碎片化主要由以下几个原因导致&#xff1a; 插入、更新和删除操作&#xff1a;当对表中的数据进行插入、更新或删除操作时&#xff0c;索引也需要相应地更新。这些DML操作可…...

Java必刷入门递归题×5(内附详细递归解析图)

目录 1.求N的阶乘 2.求12...N的和 3.顺序打印数字的每一位 4.求数字的每一位之和 5.求斐波拉契数列 1.求N的阶乘 &#xff08;1&#xff09;解析题目意思 比如求5的阶乘&#xff0c;符号表示就是5&#xff01;&#xff1b;所以5&#xff01;5*4*3*2*1我们下面使用简单的…...

android 闪屏图适配尺寸

不同的 Android 设备可能具有不同的屏幕尺寸和分辨率&#xff0c;因此最好提供不同尺寸的启动画面图像&#xff0c;以确保与各种设备的兼容性。 以下是 Android 启动画面图像的一些最常见尺寸&#xff1a; 320 x 480像素&#xff08;肖像&#xff09; 480 x 320像素&#xff0…...

正则表达式中(?s)与(?m)的区别

理论&#xff1a; (?m) 和 (?s) 是正则表达式中的两个模式标志&#xff0c;它们具有不同的作用&#xff1a; (?m) 多行模式标志&#xff08;也称为 “multiline” 模式&#xff09;&#xff1a; 默认情况下&#xff0c;正则表达式将整个输入字符串视为单行多行文本中使用…...

Clickhouse学习笔记(11)—— 数据一致性

使用合并树引擎时&#xff0c;无论是ReplacingMergeTree还是SummingMergeTree&#xff0c;都只能保证数据的最终一致性&#xff0c;因为数据的去重、聚合等操作会在数据合并的期间进行&#xff0c;而合并会在后台以一个不确定的时间进行&#xff0c;因此无法预先计划&#xff1…...

【uniapp】六格验证码输入框实现

效果图 代码实现 <view><view class"tips">已发送验证码至<text class"tips-phone">{{ phoneNumber }}</text></view><view class"code-input-wrap"><input class"code-input" v-model"…...

【react hook】在react hook组件中,使用Antd Table组件,columns数据异步获取,list数据更新但没有rerender解决办法

情景描述 我们有一个react组件&#xff0c;显示了一个Antd Table组件&#xff0c;设置了一个columns变量并复制给Table的columns属性&#xff0c;由于我们请求的datasource来源是异步的&#xff0c;示例伪代码如下&#xff1a; const [columns, setColumns] useState([]); /…...

ChatGPT的图识别来了

前几天ChatGPT推出了Dall-E 3功能&#xff0c;可以根据文字和描述一段话来生成一个或者一组图。 这次又来重磅了&#xff0c;图识别又来了&#xff01;换句话说&#xff0c;也即是文生图&#xff0c;图生文都可以实现了&#xff0c;一起来试试 1、解释图中的意思 &#xff0…...

java Stream编程笔记

文章目录 Stream介绍什么是 Stream&#xff1f; Stream中间操作过滤操作&#xff08;filter&#xff09;映射操作&#xff08;map&#xff09;排序操作&#xff08;sorted&#xff09;截断操作&#xff08;limit 和 skip&#xff09; Stream 的终止操作forEach 和 peek聚合操作…...

顶顶通语音识别使用说明

介绍 顶顶通语音识别软件(asrproxy)是一个对接了多种语音识别接口的语音识别系统。可私有化部署(支持中文英文和方言等&#xff0c;支持一句话识别、实时流识别、多声道录音文件识别。 原理 asrproxy内嵌了阿里达摩院的开源语音识别工具包FunASR,后续我们也会使用自有的预料…...

重磅发布 OpenAI 推出用户自定义版 ChatGPT

文章目录 重磅发布 OpenAI 推出用户自定义版 ChatGPT个人简介 重磅发布 OpenAI 推出用户自定义版 ChatGPT OpenAI 首届开发者大会 (OpenAI DevDay) 于北京时间 11 月 7 日凌晨 02:00 开始&#xff0c;大会上宣布了一系列平台更新。其中一个重要更新是用户可以创建他们自己的自定…...

Java 幼儿园(20231111)读取 json 文件

1、功能场景 &#xff08;1&#xff09;多人合作开发一个功能模块时&#xff0c;需要调用外部接口 &#xff08;2&#xff09;对方接口的开发工作还没有完成&#xff0c;只能提供一个返回值的示例文件 json 文件。 &#xff08;3&#xff09;返回的 json 数据多达几百个字段。 …...

云计算、大数据技术的智慧工地,实现对建筑工地实时监测、管理和控制的一种新型建筑管理方式

智慧工地是利用物联网、云计算、大数据等技术&#xff0c;实现对建筑工地实时监测、管理和控制的一种新型建筑管理方式。 智慧工地架构&#xff1a; 1、终端层&#xff1a; 充分利用物联网技术、移动应用、智能硬件设备提高现场管控能力。通过RFID、传感器、摄像头、手机等终…...

功能案例 -- 通过开关,改变白天和黑夜

效果展示 代码展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><style>:root {--default-bac-color: #f…...

Linux编写一个极简版本的Shell

Linux编写一个极简版本的Shell &#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;Linux &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 本博客主要内容在Linux环境下&#xff…...

亚马逊云AI应用科技创新下的Amazon SageMaker使用教程

目录 Amazon SageMaker简介 Amazon SageMaker在控制台的使用 模型的各项参数 pytorch训练绘图部分代码 Amazon SageMaker简介 亚马逊SageMaker是一种完全托管的机器学习服务。借助 SageMaker&#xff0c;数据科学家和开发人员可以快速、轻松地构建和训练机器学习模型&#…...

零代码部署EVA-01:5分钟体验Qwen2.5-VL机甲风格AI图片问答

零代码部署EVA-01&#xff1a;5分钟体验Qwen2.5-VL机甲风格AI图片问答 1. 初识EVA-01视觉神经同步系统 想象一下&#xff0c;当你上传一张图片后&#xff0c;一个充满机甲风格的AI界面不仅能准确识别图片内容&#xff0c;还能像人类一样理解图片背后的逻辑关系——这就是EVA-…...

Phi-4-reasoning-vision-15B惊艳效果:科研论文图表截图→统计方法识别+结论可信度评估

Phi-4-reasoning-vision-15B惊艳效果&#xff1a;科研论文图表截图→统计方法识别结论可信度评估 1. 视觉推理新标杆&#xff1a;Phi-4-reasoning-vision-15B 微软在2026年3月发布的Phi-4-reasoning-vision-15B模型&#xff0c;正在重新定义科研工作者的文献阅读方式。这个视…...

【AI】从零到一:手把手搭建PyTorch+CUDA深度学习开发环境

1. 深度学习开发环境搭建全景图 刚接触深度学习的同学往往会被各种术语搞晕——显卡驱动、CUDA、cuDNN、PyTorch&#xff0c;这些组件之间到底什么关系&#xff1f;简单来说&#xff0c;它们就像盖房子的四层地基&#xff1a;显卡驱动是地基中的钢筋&#xff0c;CUDA是混凝土框…...

Vue + G 实战:打造高校学生打卡数据可视化大屏米

1、普通的insert into 如果&#xff08;主键/唯一建&#xff09;存在&#xff0c;则会报错 新需求&#xff1a;就算冲突也不报错&#xff0c;用其他处理逻辑 回到顶部 2、基本语法&#xff08;INSERT INTO ... ON CONFLICT (...) DO (UPDATE SET ...)/(NOTHING)&#xff09; 语…...

Rust async trait 的底层调度逻辑解析

Rust async trait 的底层调度逻辑解析 Rust 的异步编程模型以其高效和灵活著称&#xff0c;而 async trait 作为异步编程的核心抽象之一&#xff0c;其底层调度逻辑直接影响性能与资源利用率。理解其工作机制不仅能帮助开发者写出更高效的代码&#xff0c;还能避免常见的并发陷…...

Unity小白也能搞定的原神桌宠:从PMX模型到可拖拽交互的完整实现(附避坑点)

Unity小白也能搞定的原神桌宠&#xff1a;从PMX模型到可拖拽交互的完整实现&#xff08;附避坑点&#xff09; 1. 准备工作与环境搭建 作为一个Unity初学者&#xff0c;想要制作一个原神风格的桌宠&#xff0c;首先需要准备好必要的工具和环境。这个过程可能会让新手感到有些迷…...

Golang Web 前后端分离企业级后台开发项目计划书V2.0模型代码

Golang Web 前后端分离企业级后台开发项目计划书V2.0模型代码 rbac.go代码 package modelimport ("time""gorm.io/gorm" )// User 用户表 type User struct {ID int gorm:"primarykey;comment:用户ID"Username string gorm:"type:…...

等保三级 + 密评双达标:SQL Server TDE + 脱敏最佳实践

一、一次审计惊魂&#xff1a;备份硬盘丢失&#xff0c;患者数据险遭泄露去年底&#xff0c;我院一台 SQL Server 2019 备份服务器因机房搬迁&#xff0c;一块存有全量患者数据的硬盘意外遗失。虽未确认是否被恶意获取&#xff0c;但根据《个人信息保护法》第51条&#xff1a;“…...

[AI/应用/MCP] MCP Server/Tool 开发指南腋

简介 langchain专门用于构建LLM大语言模型&#xff0c;其中提供了大量的prompt模板&#xff0c;和组件&#xff0c;通过chain(链)的方式将流程连接起来&#xff0c;操作简单&#xff0c;开发便捷。 环境配置 安装langchain框架 pip install langchain langchain-community 其中…...

前端内存泄漏排查指南:Chrome DevTools高级用法

前端内存泄漏排查指南&#xff1a;Chrome DevTools高级用法 在现代前端开发中&#xff0c;内存泄漏是一个常见但棘手的问题。随着单页应用&#xff08;SPA&#xff09;的普及&#xff0c;长时间运行的JavaScript代码可能导致内存占用持续增长&#xff0c;最终影响页面性能甚至…...