skynet 源码阅读 -- 核心概念服务 skynet_context
本文从 Skynet 源码层面深入解读 服务(Service) 的创建流程。从最基础的概念出发,逐步深入 skynet_context_new
函数、相关数据结构(skynet_context
, skynet_module
, message_queue
等),并通过流程图、结构图、以及源码片段的细节分析,希望能对 Skynet 服务的创建有一个由浅入深的系统认识。
1. 前言
在 Skynet 中,“服务(Service)”是最核心的概念之一。它将所有逻辑视为一个个独立且消息驱动的“小进程”,每个服务在单线程上下文中处理自己的消息队列。
- 你可以把“服务”理解成“Actor”或“轻量进程”。类似于 erlang。
- 每个服务都有自己的
skynet_context
、自己的消息队列message_queue
、以及对应的 Lua/C 实例(skynet_module_instance_create
)等,对于 lua 服务而言,mod 就是service_snlua。 - 当你在 Lua 代码里调用
skynet.newservice("xxx")
或skynet.uniqueservice("xxx")
时,底层就是通过类似skynet_context_new
来完成实际创建。所以。服务在 c 层次就是skynet_context。
本篇将系统地分析服务创建过程:从查询 module、创建 context 到初始化,并将此过程与 Skynet 的“消息驱动”模型联系起来,期望让读者对 Skynet 源码进一步了解与掌握。
2. 服务概念与 Skynet 设计哲学
- 消息驱动模型
Skynet 采用Actor模式,每个服务独立处理消息,每个服务都有自己的消息队列;服务之间通过消息队列和handle进行通信。类似的,在erlang 当中,进程间也是通过 message 通信。 - 轻量级“上下文”
每个服务不需要独立进程或操作系统线程,而是共享工作线程,因此需要用skynet_context
维护服务状态。正如 启动主流程 文中的分析,woker 线程的数量是在启动时候读配置固定创建的,而服务动态创建。 - C/S“模块”
许多服务可以基于 Lua 脚本(snlua
)、或者 C 模块(cservice
等),都由skynet_module
统一加载。
在这样的设计下,“服务创建”过程就成了将一个 module 实例绑定到一个 skynet_context上,并分配消息队列、handle、以及进行init回调的过程。
3. 服务创建的整体流程概览
下面是简单的整体概览,后文会更深入解析:
- 查找 Module:
skynet_module_query(name)
-> 得到struct skynet_module *mod
- 创建 Module 实例:
skynet_module_instance_create(mod)
-> (通常是调用 mod->create) - 分配 skynet_context:
skynet_malloc(sizeof(*ctx))
- 注册 handle:
ctx->handle = skynet_handle_register(ctx)
- 创建消息队列:
ctx->queue = skynet_mq_create(ctx->handle)
- 调用 module init 回调:
skynet_module_instance_init(mod, inst, ctx, param)
- 若 init成功 -> 将服务的消息队列加入全局消息队列 -> 返回
ctx
; 否则 -> 清理并返回NULL
这就是skynet_context_new
函数的最核心逻辑,也就是服务在底层C层面被创建出来的过程。
4. 关键数据结构分析
4.1 Skynet Context(skynet_context
)
struct skynet_context {void * instance; // 对应 module 的具体实例指针,c 服务或者是 lua 沙盒服务struct skynet_module * mod; // 指向加载的 modulevoid * cb_ud; // user data for callbackskynet_cb cb; // callback functionstruct message_queue *queue; // 该服务的消息队列ATOM_POINTER logfile; // 日志文件指针(原子操作)uint64_t cpu_cost; // 用于统计消耗uint64_t cpu_start; // 开始时cpu时间char result[32];uint32_t handle; // 唯一 handle IDint session_id; // 记录当前 sessionATOM_INT ref; // 引用计数int message_count; // 处理消息计数bool init; // 是否完成 initbool endless; // 是否endlessbool profile; // 是否启用profileCHECKCALLING_DECL // debug calling
};
要点:
handle
:Skynet 用一个全局Handle表(skynet_handle
)来标识服务,handle
即此服务ID。instance
:每个服务都有一个“module实例”指针(可能是C struct或Lua VM)cb
:当队列里有消息时,会调用cb(ctx, ud, type, session, msg, sz)
这样的callback。queue
:指向自己专属的消息队列。ref
:原子引用计数, 用来安全释放 context。
4.2 Skynet Module(skynet_module
)
struct skynet_module {const char * name;void * module;skynet_dl_create create;skynet_dl_init init;skynet_dl_release release;skynet_dl_signal signal;
};
- module:可理解为“动态库(.so) + 相关函数指针”
- create, init, release, signal:函数指针, 用于C服务的生命周期管理。
- create: 创建实例
- init: 初始化该实例
- release: 释放
- signal: 处理信号
4.3 Message Queue(message_queue
)
struct message_queue {struct spinlock lock;uint32_t handle;int cap;int head;int tail;int release;int in_global;int overload;int overload_threshold;struct skynet_message *queue;struct message_queue *next;
};
handle
: 表示这个队列属于哪个服务queue[]
: 存放实际消息(结构:skynet_message
)head, tail, cap
: 环形队列实现release
: 标记是否已释放lock
: 自旋锁 保护并发(可能worker在 pop / push)
当Worker线程要向这个服务发送消息时,会push消息进 ctx->queue
;当该服务执行时,会 pop 消息并调用 ctx->cb
处理。
5. 深度解读 skynet_context_new
下面是部分源码节选,并逐段说明:
struct skynet_context *
skynet_context_new(const char * name, const char *param) {// Step 1) 查找 modulestruct skynet_module * mod = skynet_module_query(name);if (mod == NULL)return NULL;// Step 2) 创建 module 实例void *inst = skynet_module_instance_create(mod);if (inst == NULL)return NULL;// Step 3) 分配 skynet_contextstruct skynet_context * ctx = skynet_malloc(sizeof(*ctx));CHECKCALLING_INIT(ctx)ctx->mod = mod;ctx->instance = inst;ATOM_INIT(&ctx->ref , 2);ctx->cb = NULL;ctx->cb_ud = NULL;ctx->session_id = 0;// ...// Step 4) 注册 handle, 并创建消息队列ctx->handle = skynet_handle_register(ctx);struct message_queue * queue = ctx->queue = skynet_mq_create(ctx->handle);// Step 5) 调用 module 的 init 回调int r = skynet_module_instance_init(mod, inst, ctx, param);if (r == 0) {struct skynet_context * ret = skynet_context_release(ctx);if (ret) {ctx->init = true;}skynet_globalmq_push(queue);if (ret) {skynet_error(ret, "LAUNCH %s %s", name, param ? param : "");}return ret;} else {// Step 6) 失败处理:清理skynet_error(ctx, "FAILED launch %s", name);uint32_t handle = ctx->handle;skynet_context_release(ctx);skynet_handle_retire(handle);struct drop_t d = { handle };skynet_mq_release(queue, drop_message, &d);return NULL;}
}
5.1 Step 1:skynet_module_query(name)
- 在 Skynet 初始化时,
skynet_module_init
中会加载可用模块列表(记录name->dlopen() + create/init...
)。 skynet_module_query(name)
就是根据字符串(如 "logger", "snlua", "cservice_xxx")来找到struct skynet_module *
.- 若没找到 => 返回 NULL => 创建失败。
5.2 Step 2:skynet_module_instance_create(mod)
- 调用
mod->create
指针(由 C服务实现), 这通常会返回一个“实例”指针。- 例如 logger 服务就返回 logger对象, snlua 服务就返回 snlua对象(Lua VM).
5.3 Step 3:分配 skynet_context
skynet_malloc(sizeof(*ctx))
=> 得到一个新的skynet_context
- 初始化
ctx->mod = mod; ctx->instance = inst; ref=2; ...
- ref=2:初始引用计数为2, 这表示1是自己 + 1是别的地方使用(具体可参看
skynet_context_release
机制)
- ref=2:初始引用计数为2, 这表示1是自己 + 1是别的地方使用(具体可参看
ctx->init=false
=> 还没完成初始化
5.4 Step 4:注册 handle & 创建队列
ctx->handle = skynet_handle_register(ctx)
- 这里会到全局 Handle 管理中找一个新的id(如 #10, #11 ...), 并把
(handle -> ctx)
存起来。
- 这里会到全局 Handle 管理中找一个新的id(如 #10, #11 ...), 并把
ctx->queue = skynet_mq_create(ctx->handle)
- 新建一个
message_queue
,并设置handle = ctx->handle
- 这样后续发往这个 handle 的消息,会 push 到
ctx->queue
.
- 新建一个
5.5 Step 5:调用 module init 回调
int r = skynet_module_instance_init(mod, inst, ctx, param);
- 这会执行
mod->init(inst, ctx, param)
, mod 初始化。
- 这会执行
- 如果
r == 0
, 表示init成功skynet_context_release(ctx)
=> 在成功情况下会减少ref计数, 可能最终 ref=1 => 也可让 context 继续活着.ctx->init=true
=> 标记 init成功skynet_globalmq_push(queue)
=> 把这个队列推到全局队列 => 后面 Worker 线程会处理它的消息- 最后返回
ctx
5.6 Step 6:失败处理
- 若 init 返回非0 => 说明启动失败
- 打印 “FAILED launch name”
skynet_context_release(ctx)
=> 释放 contextskynet_handle_retire(handle)
=> 将 handle 标记为“废弃”skynet_mq_release(queue, ...)
=> 释放队列, 并尝试 drop 未处理消息- 返回 NULL
通过这样一步步的流程, 该函数就创建(或失败)一个新的Skynet服务。
6. 服务初始化与消息分发简述
- 当
skynet_context_new
成功返回后,Worker 线程就能从global queue中发现该服务的消息队列 => 开始分发消息 ctx->cb
(回调)在 init 里可能被设定(例如snlua
里 skynet_callback(ctx, l , launch_cb);), 之后 Worker 线程拿到消息, 就会调用 cb(ctx, cb_ud, msg...)
这就是Skynet的服务模型:
- 每个服务都对应一个
skynet_context
- 消息放到
message_queue
=> Worker 线程调cb()
=> 处理
7. 流程图:Service Creation
以下是简易时序图(ASCII示意):
+-----------------------------+| skynet_module_query(name) |v |
[No mod? -> return NULL] |
+-----------------------------+ |
| skynet_module_instance_create(mod) |
+-----------------------------+ || (inst) |v |
+-----------------------------+ |
| ctx = skynet_malloc(...) |
| ctx->mod = mod; ctx->instance=inst |
+-----------------------------+ || |v |
+-----------------------------+ |
| ctx->handle = skynet_handle_register(ctx)
| ctx->queue = skynet_mq_create(handle)
+-----------------------------+ || |v |
+-------------------------------------------+
| r = skynet_module_instance_init(mod,...) |
+-------------------------------------------+| if (r==0) success | else fail| |success---+ +-----> fail:set ctx->init=true retire handleglobalmq_push(queue) release queuereturn ctx return NULL
8. 源码走读与关键步骤详解
8.1 skynet_module_query
- 定位在
skynet_module.c
,内部维护一个static struct modules *M
全局结构,里面记录已经加载的C服务。 skynet_module_query(name)
就遍历 M->m[] 里找module->name == name
=> 返回指针.- 若找不到 => 返回NULL.
8.2 skynet_module_instance_create(mod)
mod->create(...)
常见写法是在.so
里导出xxx_create
函数 => 分配C struct- 例如
snlua_create
会新建一个struct snlua
(包含lua_State) - 这一步并没有调用 init, 只是一种“先建实例,再init”的两段式构造.
8.3 skynet_handle_register
skynet_handle_register(ctx)
就返回一个全局唯一的 handle(>=1).- 以后发送消息时, 只需
skynet_send( to_handle, ... )
, Skynet可以reverse handle->context => 找到to_ctx->queue
.
8.4 skynet_mq_create
mq = skynet_malloc(sizeof(*mq)) + ...
=> init capacity, lock=0, handle=..., etc.- 这样一个空队列就和
ctx->handle
绑定 - 之后push消息 =>
mq->queue[tail] = message; tail=(tail+1)%cap;
8.5 skynet_module_instance_init
r = mod->init(inst, ctx, param)
- 这是C服务具体写的init函数, 可能做Lua加载脚本, 也可能加载资源, etc.
9. 服务创建中涉及的锁与引用计数
- 锁:
spinlock lock
在message_queue
or globalmq 里保证并发安全- 在
skynet_context_new
里不怎么显式使用锁,但内部handle_register & mq_create 都会用到全局/队列锁
- 引用计数(
ATOM_INT ref
):- 初始=2 => 代表这个 context 还有2个持有者:
- handle_storage 全局
- 自己(在 new 函数中)
skynet_context_release(ctx)
=> ref-- => 如果==0 => free context- 这样可以保证在init失败或成功后 context 处理都不会重复释放.
- 初始=2 => 代表这个 context 还有2个持有者:
10. Skynet 服务模型的优势与局限
- 优势
- Actor 模式,context + queue => 易于并发下消息封装
- 强大的灵活性:C服务 / Lua服务都可 => 只要 module create + init + release
- 在游戏业务当中就是 不同的业务可以创建不同的 服务去处理,天然隔离,不用考虑并发问题,数据都是隔离的,仅通过消息传递。
- 局限
- 需要对消息无共享(Actor风格), 适合“异步消息驱动”
- 单个服务最多占用单线程 的 cpu,slg 大地图场景下,如果服务不好拆分,但是又需要大量计算的场景可能会有些麻烦。在java 中,可以直接起线程池,并行计算。skynet 中当然也可以启动 服务池 并行计算,但是数据的共享又会是个问题,后续研究:
第一种:消息传递只传递指针,而不对消息进行拷贝。可行吗?
第二种:共享内存来进行数据传递。
11. 总结
skynet_context_new
正是 Skynet 服务创建的核心。它背后包含模块系统(C服务管理)、Handle系统(服务ID分配)、消息队列系统(异步驱动),以及初始化回调(每种服务各自逻辑)等多个模块协同。
通过数据结构( skynet_context
, skynet_module
, message_queue
)与关键流程( module_instance_create
, handle_register
...) 的介绍,我们可以看到Skynet对“服务”这一抽象的高内聚设计——一个 context就是一个服务**:
- Module表征它的实现( C or Lua )
- Context整合状态(handle, instance, queue, cb...)
- Queue存放消息
- init / release 是服务的生命周期
后续深入阅读,追踪消息是如何被投递到服务并由Worker线程执行,则可以继续研究**skynet_context_message_dispatch
等函数;如果你想了解C服务如何编写 module,则可以研究skynet_module.c
** 以及具体 cservice 示例(logger.c
, service_snlua.c
等)。
相关文章:
skynet 源码阅读 -- 核心概念服务 skynet_context
本文从 Skynet 源码层面深入解读 服务(Service) 的创建流程。从最基础的概念出发,逐步深入 skynet_context_new 函数、相关数据结构(skynet_context, skynet_module, message_queue 等),并通过流程图、结构…...
前端react后端java实现提交antd form表单成功即导出压缩包
前端(React Ant Design) 1. 创建表单:使用<Form>组件来创建你的表单。 2. 处理表单提交:在onFinish回调中发起请求到后端API,并处理响应。 import React from react; import { Form, Input, Button } from ant…...
2025 = 1^3 + 2^3 + 3^3 + 4^3 + 5^3 + 6^3 + 7^3 + 8^3 + 9^3
【算法代码】 #include <bits/stdc.h> using namespace std;int year2025; int main() {cout<<year<<" ";int i1;while(year) {cout<<i<<"^3";if(i<9) cout<<" ";year-pow(i,3);i;}return 0; }/* 202…...
程序代码篇---C++常量引用
文章目录 前言第一部分:C常量常量变量const与指针1.指向常量的指针2.常量指针3.指向常量的常量指针 常量成员函数const_cast运算符总结 第二部分:C引用引用的基本概念引用的声明引用的使用引用的特性1.不可变性2.无需解引用3.内存地址 引用的用途1.函数参…...
DeepSeek-R1本地部署笔记
文章目录 效果概要下载 ollama终端下载模型【可选】浏览器插件 UIQ: 内存占用高,显存占用不高,正常吗 效果 我的配置如下 E5 2666 V3 AMD 590Gme 可以说是慢的一批了,内存和显卡都太垃圾了,回去用我的新设备再试试 概要 安装…...
golang通过AutoMigrate方法自动创建table详解
一.AutoMigrate介绍 1.介绍 在 Go 语言中,GORM支持Migration特性,支持根据Go Struct结构自动生成对应的表结构,使用 GORM ORM 库的 AutoMigrate 方法可以自动创建数据库表,确保数据库结构与定义的模型结构一致。AutoMigrate 方法非常方便&am…...
如何看待 OpenAI 的12天“shipmas”发布计划?
openAI的“Shipmas”并非单纯的营销活动,而是在用户增长、技术创新和市场竞争中的综合布局和战略体现。 史上最寒酸的发布会?继十月马斯克在好莱坞电影城高调发布特斯拉三款最新产品(无人出租车、无人巴士、人形机器人)后,十二月,OpenAI CEO 奥特曼宣布 OpenAI 将连续12…...
Vue 3 30天精进之旅:Day 07 - Vue Router
引言 在前几天的学习中,我们深入探讨了Vue的表单输入绑定及其处理机制。今天,我们将学习Vue Router,这是Vue.js官方提供的路由管理器,用于构建单页面应用(SPA)。通过使用Vue Router,你可以轻松…...
Redis学习之哨兵二
一、API 1.sentinel masters:展示被监控的主节点状态及相关的统计信息 2.sentinel master <master name>:展示指定的主节点的状态以及相关的统计信息 3.sentinel slaves <master name>:展示指定主节点的从节点状态以及相关的统计信息 4.sentinel sentinels <mas…...
本地部署AI大模型(deepseek r1)说明
1、硬件环境和操作系统 win11intel i5 8C16G deepseek-1.5B和7B**:选择RTX 3060或其他 Pascal架构显卡,搭配至少8GB的内存。 deepseek-70B**:推荐使用RTX 40系列 APU 或更高世代显卡,…...
幸运数字——蓝桥杯
1.问题描述 哈沙德数是指在某个固定的进位制当中,可以被各位数字之和整除的正整数。例如 126126 是十进制下的一个哈沙德数,因为 (126)10mod(126)0;126 也是八进制下的哈沙德数,因为 (126)10(176)8,(126)10mod(176)…...
[250129] Archinstall 3.0.2 发布 | Wolfram 语言与 Mathematica 14.2 版本发布
目录 Archinstall 3.0.2 版本发布Wolfram 语言与 Mathematica 14.2 版本发布🌟 主要亮点 Archinstall 3.0.2 版本发布 Archlinux 的自动化安装程序 Archinstall 发布了 3.0.2 版本,该版本带来了大量的改进和修复,以及一些新功能和贡献者。 …...
ios swift画中画技术尝试
继上篇:iOS swift 后台运行应用尝试失败-CSDN博客 为什么想到画中画,起初是看到后台模式里有一个picture in picture,去了解了后发现这个就是小窗口视频播放,方便用户执行多任务。看小窗口视频的同时,可以作其他的事情…...
第21节课:前端构建工具—自动化与模块化的利器
目录 前端构建工具的重要性任务运行器:Gulp与GruntGulpGulp的工作原理安装与使用Gulp GruntGrunt的工作原理安装与使用Grunt 模块打包器:WebpackWebpack简介Webpack的工作原理安装与使用Webpack 实践:使用Gulp和Webpack构建前端项目示例&…...
python3+TensorFlow 2.x 基础学习(一)
目录 TensorFlow 2.x基础 1、安装 TensorFlow 2.x 2、TensorFlow 2.x 基础概念 2、1 Eager Execution 2、2 TensorFlow 张量(Tensor) 3、使用Keras构建神经网络模型 3、1 构建 Sequential 模型 3、2 编译模型 1、Optimizer(优化器&a…...
在sortablejs的拖拽排序情况下阻止input拖拽事件
如题 问题 在vue3的elementPlus的table中,通过sortablejs添加了行拖拽功能,但是在行内会有输入框,此时拖拽输入框会触发sortablejs的拖拽功能 解决 基于这个现象,我怀疑是由于拖拽事件未绑定而冒泡到后面的行上从而导致的拖拽…...
doris:异常数据处理
在导入过程中,源数据列与目标列的数据类型可能存在不一致的情况。导入过程会对这些类型不一致的数据进行转换,但在转换过程中可能会出现字段类型不匹配、字段超长、精度不匹配等问题,从而导致转换失败。 为了处理这些异常情况,Do…...
汇编基础语法及其示例
1.汇编指令 1.1汇编指令的基本格式 <opcode>{<cond>}{s} <Rd> , <Rn> , <shifter_operand> <功能码>{<条件码>}{cpsr影响位} <目标寄存器> , <第一操作寄存器> , <第二操作数> 注:第一操作寄存器…...
使用python调用JIRA6 进行OAuth1认证获取AccessToken
Jira配置应用程序链接 1) 创建应用程序链接 登录 JIRA 管理后台。转到 Administration > Applications > Application Links。在输入框中输入外部应用程序的 URL(例如 GitLab 或自定义应用),然后点击 Create new link。 2) 配置 Con…...
关于el-table翻页后序号列递增的组件封装
需求说明: 项目中经常会用到的一个场景,表格第一列显示序号(1、2、3...),但是在翻页后要递增显示序号,例如10、11、12(假设一页显示10条数据),针对这种情况,封…...
Android createScaledBitmap与Canvas通过RectF drawBitmap生成马赛克/高斯模糊(毛玻璃)对比,Kotlin
Android createScaledBitmap与Canvas通过RectF drawBitmap生成马赛克/高斯模糊(毛玻璃)对比,Kotlin import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Canvas import android.graphics.RectF …...
Linux 进程概念
目录 一、前言 二、概念实例,正在执行的程序等 三、描述进程-PCB 四、组织进程 五、查看进程 编辑六、通过系统调用获取进程标示符 七、进程切换和上下文数据 1.进程切换 2.上下文数据 一、前言 在Linux中,每个执行的程序叫做进程ÿ…...
Kafa分区策略实现
引言 Kafka 的分区策略决定了生产者发送的消息会被分配到哪个分区中,合理的分区策略有助于实现负载均衡、提高消息处理效率以及满足特定的业务需求。 轮询策略(默认) 轮询策略是 Kafka 默认的分区策略(当消息没有指定键时&…...
元素的显示与隐藏
display显示隐藏visibility显示隐藏overflow溢出显示隐藏 display属性 visibility属性 overflow溢出...
“AI视频智能分析系统:让每一帧视频都充满智慧
嘿,大家好!今天咱们来聊聊一个特别厉害的东西——AI视频智能分析系统。想象一下,如果你有一个超级聪明的“视频助手”,它不仅能自动识别视频中的各种元素,还能根据内容生成详细的分析报告,是不是感觉特别酷…...
LeetCode:63. 不同路径 II
跟着carl学算法,本系列博客仅做个人记录,建议大家都去看carl本人的博客,写的真的很好的! 代码随想录 LeetCode:63. 不同路径 II 给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0]…...
P4681 [THUSC 2015] 平方运算 Solution
Description 给定序列 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1,a2,⋯,an) 和常数 p p p ,有 m m m 个操作,分以下两种: modify ( l , r ) \operatorname{modify}(l,r) modify(l,r):对每个 i ∈ [ …...
机器人抓取与操作概述(深蓝)——1
工业机器人:① “臂”的形态 ② “手”的形态 ③ 视觉,力和触觉 1 机器人的不同形态 “臂”的形态 “手”的形态 2 常见的操作任务 操作:插入、推和滑 抓取:两指(平行夹爪)抓取、灵巧手抓取 落地-产…...
算法基础学习——快排与归并(附带java模版)
快速排序和归并排序是两种速度较快的排序方式,是最应该掌握的两种排序算法, (一)快速排序(不稳定的) 基本思想:分治 平均时间复杂度:O(nlogn) / 最慢O(n^2) / 最快O(n) 步骤&…...
JavaScript_02 表单
表单常用演示: 1.图片 结果失真了... 2.切换图片 切换结果 3.表单:...
接口 V2 完善:分布式环境下的 WebSocket 实现与 Token 校验
🎯 本文档详细介绍了如何使用WebSocket协议优化客户端与服务端之间的通信,特别是在处理异步订单创建通知的场景中。通过引入WebSocket代替传统的HTTP请求-响应模式,实现了服务器主动向客户端推送数据的功能,极大地提高了实时性和效…...
Android中Service在新进程中的启动流程2
目录 1、Service在客户端的启动入口 2、Service启动在AMS的处理 3、Service在新进程中的启动 4、Service与AMS的关系再续 上一篇文章中我们了解了Service在新进程中启动的大致流程,同时认识了与客户端进程交互的接口IApplicationThread以及与AMS交互的接口IActi…...
C语言初阶力扣刷题——349. 两个数组的交集【难度:简单】
1. 题目描述 力扣在线OJ题目 给定两个数组,编写一个函数来计算它们的交集。 示例: 输入:nums1 [1,2,2,1], nums2 [2,2] 输出:[2] 输入:nums1 [4,9,5], nums2 [9,4,9,8,4] 输出:[9,4] 2. 思路 直接暴力…...
Java 大视界 -- Java 大数据在自动驾驶中的数据处理与决策支持(68)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
DeepSeek学术写作测评第二弹:数据分析、图表解读,效果怎么样?
我是娜姐 迪娜学姐 ,一个SCI医学期刊编辑,探索用AI工具提效论文写作和发表。 针对最近全球热议的DeepSeek开源大模型,娜姐昨天分析了关于论文润色、中译英的详细效果测评: DeepSeek学术写作测评第一弹:论文润色&#…...
(2)SpringBoot自动装配原理简介
SpringBoot自动装配 这里写目录标题 SpringBoot自动装配启动器主程序自定义扫描包SpringBootApplicationSpringBootConfigurationEnableAutoConfigurationAutoConfigurationPackageImport({AutoConfigurationImportSelector.class})选择器AutoConfigurationEntrygetCandidateCo…...
Rust:Rhai脚本编程示例
当然,以下是一个简单的Rhai脚本编程示例,展示了如何在Rust中使用Rhai执行脚本。 首先,你需要确保你的Rust项目中包含了rhai库。你可以在你的Cargo.toml文件中添加以下依赖项: [dependencies] rhai "0.19" # 请检查最…...
深入理解文件描述符
问题 文件描述符只是一个整数值,那么系统是如何利用这个整数值来完成文件读写的呢? 什么是文件系统? 计算机中用于组织、存储和管理文件的数据结构集合 管理磁盘或其他存储介质上的空间 (将存储介质分块管理)保证文件数据不被破坏…...
使用CSS实现一个加载的进度条
文章目录 使用CSS实现一个加载的进度条一、引言二、步骤一:HTML结构与CSS基础样式1、HTML结构2、CSS基础样式 三、步骤二:添加动画效果1、使用CSS动画2、结合JavaScript控制动画 四、使用示例五、总结 使用CSS实现一个加载的进度条 一、引言 在现代网页…...
SQL 指南
SQL 指南 引言 SQL(Structured Query Language,结构化查询语言)是一种用于管理关系数据库系统的标准计算机语言。自1970年代问世以来,SQL已经成为了数据库管理和数据操作的事实标准。本文旨在为初学者和有经验的数据库用户提供一个全面的SQL指南,涵盖SQL的基础知识、高级…...
sqlzoo答案4:SELECT within SELECT Tutorial
sql练习:SELECT within SELECT Tutorial - SQLZoo world表: namecontinentareapopulationgdpAfghanistanAsia6522302550010020343000000AlbaniaEurope28748283174112960000000AlgeriaAfrica238174137100000188681000000AndorraEurope46878115371200000…...
斐波那契数(信息学奥赛一本通-1071)
【题目描述】 菲波那契数列是指这样的数列: 数列的第一个和第二个数都为1,接下来每个数都等于前面2个数之和。给出一个正整数k,要求菲波那契数列中第k个数是多少。 【输入】 输入一行,包含一个正整数k。(1 ≤ k ≤ 46)…...
数据结构与算法再探(六)动态规划
目录 动态规划 (Dynamic Programming, DP) 动态规划的基本思想 动态规划的核心概念 动态规划的实现步骤 动态规划实例 1、爬楼梯 c 递归(超时)需要使用记忆化递归 循环 2、打家劫舍 3、最小路径和 4、完全平方数 5、最长公共子序列 6、0-1背…...
ECMAScript--promise的使用
一、Promise的简介 Promise是一个代理,它所代表的值在创建时并不一定是已知的。借助Promise,我们能够将处理程序与异步操作最终的成功值或者失败原因关联起来。这一特性使得异步方法可以像同步方法那样返回值,不同之处在于异步方法不会立…...
微服务入门(go)
微服务入门(go) 和单体服务对比:里面的服务仅仅用于某个特定的业务 一、领域驱动设计(DDD) 基本概念 领域和子域 领域:有范围的界限(边界) 子域:划分的小范围 核心域…...
【自学笔记】计算机网络的重点知识点-持续更新
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 计算机网络重点知识点一、计算机网络概述二、网络分类三、网络性能指标四、网络协议与体系结构五、数据交换方式六、物理层与数据链路层七、网络层与运输层八、应用…...
leetcode——二叉树的中序遍历(java)
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 示例 1: 输入:root [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root [] 输出:[] 示例 3: 输入:root [1] 输出…...
neo4j-community-5.26.0 install in window10
在住处电脑重新配置一下neo4j, 1.先至官方下载 Neo4j Desktop Download | Free Graph Database Download Neo4j Deployment Center - Graph Database & Analytics 2.配置java jdk jdk 21 官网下载 Java Downloads | Oracle 中国 path: 4.查看java -version 版本 5.n…...
物联网智能项目之——智能家居项目的实现!
成长路上不孤单😊😊😊😊😊😊 【14后😊///计算机爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于物联网智能项目之——智能家居项目…...
基于SpringBoot的假期周边游平台的设计与实现(源码+SQL脚本+LW+部署讲解等)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...