OpenGL查询对象 Query Objects
查询对象和异步查询(Query Objects and Asynchronous Queries)
Query Objects(查询对象)是OpenGL中的一种机制,用于获取有关一系列GL命令处理过程的信息。这些信息可以包括:
- 绘图命令处理的图元数量。
- 写入变换反馈缓冲区的图元数量。
- 在片段处理期间通过深度测试的样本数量。
- 处理命令所需的时间量。
通过使用查询对象,OpenGL应用程序可以动态地获取有关图形渲染过程的各种性能指标和统计信息。这些信息对于性能优化、调试和分析应用程序的渲染流程非常有用。
使用查询对象的一般步骤如下:
- 创建查询对象:通过调用OpenGL的API函数,可以创建一个查询对象,并指定要查询的信息类型。
- 开始查询:在需要获取信息的地方,通过调用OpenGL的API函数,开始一个查询操作。
- 执行一系列GL命令:在开始查询和结束查询之间,执行一系列的GL命令,这些命令可以是绘图命令、变换反馈命令、深度测试命令等。
- 结束查询:在执行完一系列GL命令后,通过调用OpenGL的API函数,结束查询操作。
- 获取查询结果:通过调用OpenGL的API函数,可以获取查询对象的结果,这些结果包含了在查询期间收集的有关图形处理过程的信息,比如处理的图元数量、通过的样本数量等。
使用查询对象可以帮助开发人员更好地了解和优化他们的OpenGL应用程序的性能特征,从而提高图形渲染的效率和质量。
查询类型
OpenGL 支持的查询类型包括:
-
图元生成查询:目标为
PRIMITIVES_GENERATED
,用于返回通过 OpenGL 处理的图元数量。同时激活的此类查询最多可达MAX_VERTEX_STREAMS
个。 -
变换反馈写入的图元查询:目标为
TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
,用于统计被写入一个或多个缓冲对象中的图元数量。同时激活的此类查询同样不得超过MAX_VERTEX_STREAMS
个。 -
变换反馈溢出查询:目标为
TRANSFORM_FEEDBACK_OVERFLOW
或TRANSFORM_FEEDBACK_STREAM_OVERFLOW
,用于报告一个或多个流是否存在变换反馈溢出情况。 -
遮挡查询:目标为
SAMPLES_PASSED
、ANY_SAMPLES_PASSED
或ANY_SAMPLES_PASSED_CONSERVATIVE
,用于计数通过深度测试的片段或样本数量,或者设置布尔值以表示是否至少有一个片段或样本通过了深度测试。同时只能激活一个此类查询。 -
时间流逝查询:目标为
TIME_ELAPSED
,记录完成一系列命令所需的完整时间。同时只能激活一个此类查询。 -
时间戳查询:目标为
TIMESTAMP
,记录当前 OpenGL 的时间。同时只能激活一个此类查询。 -
提交查询:目标为
VERTICES_SUBMITTED
和PRIMITIVES_SUBMITTED
分别返回传输到 OpenGL 的顶点数量和图元数量信息。 -
顶点着色器查询:目标为
VERTEX_SHADER_INVOCATIONS
,返回顶点着色器被调用的次数。 -
曲面细分着色器查询:目标为
TESS_CONTROL_SHADER_PATCHES
和TESS_EVALUATION_SHADER_INVOCATIONS
,分别返回曲面细分控制着色器处理的补丁数量和曲面细分评估着色器被调用的次数。 -
几何着色器查询:目标为
GEOMETRY_SHADER_INVOCATIONS
和GEOMETRY_SHADER_PRIMITIVES_EMITTED
,分别返回几何着色器被调用的次数以及它发射的图元数量。 -
图元裁剪查询:目标为
CLIPPING_INPUT_PRIMITIVES
和CLIPPING_OUTPUT_PRIMITIVES
,分别返回在图元裁剪阶段处理的图元数量和由图元裁剪阶段输出并进一步被光栅化阶段处理的图元数量。 -
片段着色器查询:目标为
FRAGMENT_SHADER_INVOCATIONS
,返回片段着色器被调用的次数。 -
计算着色器查询:目标为
COMPUTE_SHADER_INVOCATIONS
,返回计算着色器被调用的次数。
查询对象创建和激活
异步查询的结果不会在集合中的最后一个命令完成后立即由GL返回;在查询结果完全就绪之前,可以处理后续命令。一旦可用,查询结果将存储在相关联的查询对象中。第4.2.3节描述的命令提供了确定查询结果何时可用并返回查询的实际结果的机制。查询对象的名称空间是无符号整数,其中零被GL保留。
void glGenQueries( sizei n, uint *ids )
命令在ids中返回n个先前未使用的查询对象名称。这些名称被标记为已使用,仅用于GenQueries,但在它们首次被BeginQuery、BeginQueryIndexed或QueryCounter使用之前,它们不与任何对象关联。
void glCreateQueries( enum target, sizei n, uint *ids )
glCreateQueries在ids中返回n个先前未使用的查询对象名称,每个名称表示具有指定目标的新查询对象。目标必须是 **查询类型** 中描述的查询对象目标之一。生成的查询对象的初始状态是结果已标记为可用(查询对象的QUERY_RESULT_AVAILABLE的值为TRUE),并且结果值(QUERY_RESULT的值)为零。
void glDeleteQueries( sizei n, const uint *ids )
ids包含要删除的n个查询对象的名称。删除查询对象后,其名称再次变为未使用。如果删除了活动查询对象,则其名称立即变为未使用,但底层对象直到不再处于活动状态(参见第5.1节)才会被删除。对于GenQueries目的而标记为已使用的ids中的未使用名称将再次标记为未使用。未使用的ids中的名称将被静默忽略,零值也将被静默忽略。
在OpenGL中,除TIMESTAMP类型的计时器查询外,对于支持的每种查询类型,针对每个可能激活的查询,都有一个对应的活动查询对象名称。如果这个活动查询对象名称非零,则表示OpenGL当前正在追踪相关的信息,并且查询结果会被记录到该查询对象内。
反之,如果活动查询对象名称为零,则意味着未追踪此类信息。
创建并激活查询对象可以使用以下命令:
void glBeginQueryIndexed( enum target, uint index, uint id )
target
参数指定了要执行的查询类型,后续章节将详细介绍其有效值。- 如果
id
是一个未使用的查询对象名称,则该名称会被标记为已使用,并与指定目标类型的新的查询对象关联起来。否则,id
必须是现有且同类型查询对象的名称。请注意,通过ANY_SAMPLES_PASSED或ANY_SAMPLES_PASSED_CONSERVATIVE两种目标指定的遮挡查询对象,在未来的查询中可以重用于这两种目标中的任何一种。而由SAMPLES_PASSED目标指定的对象只能重用于该目标。 index
参数是查询的索引,其值必须在0和目标特定的最大值之间。- 无论
id
是否为新创建的查询对象,其状态都将被设置为结果不可用(查询对象的QUERY_RESULT_AVAILABLE值为FALSE),并且结果值(QUERY_RESULT)初始化为零。 - 对于给定的
target
和index
,其对应的活动查询对象名称会被设置为id
。
void glBeginQuery( enum target, uint id )
- 等价于 glBeginQueryIndexed(target, 0, id);
void glEndQueryIndexed( enum target, uint index )
-
标记了由target和index指定的活动查询要跟踪的命令序列的结束。target和index的含义与BeginQueryIndexed中相同。
-
相应的活动查询对象被更新以指示查询结果不可用,并且target和index的活动查询对象名称被重置为零。当在EndQueryIndexed调用时发出的命令完成并且最终查询结果可用时,被调用时活动的查询对象被更新以包含查询结果,并指示查询结果可用。
void glEndQuery( enum target )
- 等价于 glEndQueryIndexed(target, 0);
查询对象包含两部分状态信息:
- 一个单比特位,用于指示查询结果是否可用。
- 一个整数,用于存储查询结果的值。这个表示查询结果所使用的比特位数量(n)是实现依赖的,并且可以按照4.2.3节中描述的方式确定。
查询对象的初始状态取决于它是通过`CreateQueries`还是`BeginQueryIndexed`创建的,如上所述。如果查询结果发生溢出(即超过2^n - 1的值),其值将变为未定义。尽管不是必需的,但建议实现能够通过在达到2^n - 1时饱和并停止增加的方式来处理这种溢出情况。对于每个可能的活动查询目标和索引,都需要维护的状态包括:一个无符号整数,用于保存活动查询对象名称(如果没有活动查询对象,则为零);以及任何保持正在进行的异步查询当前结果所需的状态。同一时间内只允许一种遮挡查询类型处于活动状态,因此遮挡查询所需的必要状态会被共享。
boolean glIsQuery( uint id )
用于检查给定的ID(id)是否为一个查询对象的名称。
void glGetQueryIndexediv( enum target, uint index, enum pname, int *params )
查询有关活动查询对象的信息
-
target
和index
指定了要查询的活动查询,其含义与BeginQueryIndexed
中相同。 -
如果
pname
设置为CURRENT_QUERY
,则当前为target
和index
指定的活动查询对象名称(如果有活动查询的话),将被放置在params
指向的内存中。若目标为TIMESTAMP
,则始终返回0。 -
若
pname
设为QUERY_COUNTER_BITS
,则忽略index
参数,并将在params
中放置目标查询类型的实现依赖的查询结果所使用的比特位数。查询计数器的比特位数量可能为0,这意味着计数器不包含有用信息。
对于不同类型的查询对象,如果对应的比特位数非零,则它们至少应具备以下最低位数要求:
- 基本图元查询(PRIMITIVES_GENERATED和TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN):至少32位。
- 变换反馈溢出查询(TRANSFORM_FEEDBACK_OVERFLOW和TRANSFORM_FEEDBACK_STREAM_OVERFLOW):至少1位。
- 遮挡查询(ANY_SAMPLES_PASSED或ANY_SAMPLES_PASSED_CONSERVATIVE):至少1位。针对SAMPLES_PASSED目标的遮挡查询,至少需要32位。
- 计时器查询(TIME_ELAPSED和TIMESTAMP):至少30位,确保至少能测量一秒的时间。
- 管道统计查询(VERTICES_SUBMITTED、PRIMITIVES_SUBMITTED、VERTEX_SHADER_INVOCATIONS、TESS_CONTROL_SHADER_PATCHES、TESS_EVALUATION_SHADER_INVOCATIONS、GEOMETRY_SHADER_INVOCATIONS、FRAGMENT_SHADER_INVOCATIONS、COMPUTE_SHADER_INVOCATIONS、GEOMETRY_SHADER_PRIMITIVES_EMITTED、CLIPPING_INPUT_PRIMITIVES和CLIPPING_OUTPUT_PRIMITIVES):至少32位。
void glGetQueryiv( enum target, enum pname, int *params )
等价于 glGetQueryIndexediv(target, 0, pname, params);
查询对象的状态可以通过以下命令进行查询
void GetQueryObjectiv( uint id, enum pname, int *params );
void GetQueryObjectuiv( uint id, enum pname, uint *params );
void GetQueryObjecti64v( uint id, enum pname, int64 *params );
void GetQueryObjectui64v( uint id, enum pname, uint64 *params );
void GetQueryBufferObjectiv( uint id, uint buffer, enum pname, intptr offset );
void GetQueryBufferObjectuiv( uint id, uint buffer, enum pname, intptr offset );
void GetQueryBufferObjecti64v( uint id, uint buffer, enum pname, intptr offset );
void GetQueryBufferObjectui64v( uint id, uint buffer, enum pname, intptr offset );
其中:
id
是一个查询对象的名称。- 对于
GetQueryBufferObject*
函数,buffer
是缓冲区对象的名称,而offset
是缓冲区内写入查询值的偏移量。 - 对于
GetQueryObject*
函数,查询值可以被返回到客户端内存中,也可以写入到缓冲区对象中。如果当前绑定到查询结果缓冲区绑定点(见第6.1节中的 QUERY_RESULT)的是零,则params
被视为指向客户端内存的一个指针,在该位置写入查询值;否则,params
将被视为查询结果缓冲对象内的一个偏移量。
对于查询对象的结果值,可能需要一定时间才能变得可用。如果 pname
设置为 QUERY_RESULT_AVAILABLE
,则在需要等待时函数将返回 FALSE
;否则返回 TRUE
。必须保证,若任何查询对象返回的结果可用性为 TRUE
,那么在此之前所有相同类型的所有查询也必须返回 TRUE
。对任一查询对象反复查询 QUERY_RESULT_AVAILABLE
保证最终会返回 TRUE
。
- 若
pname
为QUERY_TARGET
,则返回查询对象的目标类型作为一个整数值。 - 若
pname
为QUERY_RESULT
,则返回查询对象的结果值作为单个整数。如果结果值的大小过大以至于无法用请求的类型表示,则返回最接近的可表示值。如果目标类型的查询计数器位数为零,则结果将以整数值0的形式返回。查询QUERY_RESULT
会使给定查询对象在有限时间内完成其计算过程。 - 若
pname
为QUERY_RESULT_NO_WAIT
,则仅当结果在执行状态查询时已可用时,才以单个整数形式返回查询对象的结果值。若结果不可用,则不写入查询返回值。
如果在调用上述查询命令之前,使用同一对象名称发起了多个查询操作,则返回的结果和可用性信息始终来自最后发起的那个查询。在开始针对同一目标和ID的新查询之前,如果不先检索先前查询的结果,这些结果将会丢失。
时间查询(Time Queries)
查询对象还可用于追踪完成一组OpenGL命令所需的时间(时间流逝查询),或确定当前的OpenGL时间(计时器查询)。
当使用目标 TIME_ELAPSED
调用 BeginQuery
和 EndQuery
时,OpenGL将准备启动和停止用于时间流逝查询的计时器。计时器会在所有先前命令对OpenGL客户端、服务器状态以及帧缓冲区的影响完全实现后开始或停止。BeginQuery
和 EndQuery
命令可能在计时器实际开始或停止之前返回。
当时间流逝查询的计时器最终停止时,流逝的时间(以纳秒为单位)会被写入到相应的查询对象作为查询结果值,并标记该对象的查询结果为可用。
可以通过以下命令创建计时器查询对象:
void QueryCounter( uint id, enum target );
其中 target
必须是 TIMESTAMP
。如果 id
是未使用的查询对象名称,则该名称会被标记为已使用并与新的类型为 TIMESTAMP
的查询对象关联。否则,id
必须是现有同类型查询对象的名称。
另外,也可以通过调用 CreateQueries
并将 target
设置为 TIMESTAMP
来创建 TIMESTAMP
类型的查询对象。
当调用 QueryCounter
时,OpenGL会记录下当前时间并将其存入对应的查询对象中。这个时间是在所有先前命令对OpenGL客户端、服务器状态及帧缓冲区的影响完全实现之后记录的。一旦时间被记录,该对象的查询结果就会被标记为可用。计时器查询可以在目标为 TIME_ELAPSED
的 BeginQuery
/ EndQuery
块内部使用,且不会影响该查询对象的结果。
通过调用 GetIntegerv
或 GetInteger64v
并传入符号常量 TIMESTAMP
,可以查询当前的OpenGL时间。这将返回所有先前命令到达OpenGL服务器但不一定执行完毕后的GL时间。通过结合这种同步获取命令和异步时间戳查询对象目标,应用程序可以测量命令从到达OpenGL服务器到在帧缓冲区实现之间的延迟。
相关文章:

OpenGL查询对象 Query Objects
查询对象和异步查询(Query Objects and Asynchronous Queries) Query Objects(查询对象)是OpenGL中的一种机制,用于获取有关一系列GL命令处理过程的信息。这些信息可以包括: 绘图命令处理的图元数量。写入变换反馈缓冲区的图元数…...

【数据分享】1929-2023年全球站点的逐日最高气温数据(Shp\Excel\免费获取)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标,其中又以气温指标最为常用!说到气温数据,最详细的气温数据是具体到气象监测站点的气温数据! 之前我们分享过1929-2023年全球气象站…...

Docker深入解析:从基础到实践
Docker基础知识 Docker是什么:定义和核心概念解释 Docker是一个开源项目,它诞生于2013年,旨在自动化应用程序的部署过程, 让应用程序能够在轻量级的、可移植的、自给自足的容器中运行。这些容器可以在几乎任何机器上运行…...

【鸿蒙】大模型对话应用(一):大模型接口对接与调试
Demo介绍 本demo对接阿里云和百度的大模型API,实现一个简单的对话应用。 DecEco Studio版本:DevEco Studio 3.1.1 Release HarmonyOS API版本:API9 关键点:ArkTS、ArkUI、UIAbility、网络http请求、列表布局 官方接口文档 此…...

SQL的函数类型
目录 一、聚合函数 二、数值型函数 三、字符串函数 四、日期函数 五、流程控制函数 一、聚合函数 定义:聚合函数是指对一组值进行运算,最终返回是单个值,也可以被称为组合函数。 COUNT() 统计目标行数量的函数 AVG() 求平均值 SU…...

TSINGSEE青犀视频智慧电梯管理平台,执行精准管理、提升乘梯安全
一、方案背景 随着城市化进程的不断加快,我国已经成为全球最大的电梯生产和消费市场,电梯也成为人们日常生活中不可或缺的一部分。随着电梯数量的激增,电梯老龄化,维保数据不透明,物业管理成本高,政府监管…...

VMware:在部分链上无法执行所调用的函数,请打开父虚拟磁
VMware:在部分链上无法执行所调用的函数,请打开父虚拟磁 问题:VMware给虚拟机扩展硬盘容量,提示:在部分链上无法执行所调用的函数,请打开父虚拟磁。原因:是因为你的虚拟磁盘文件是分多个文件存储的…...

【数据结构 08】红黑树
一、概述 红黑树,是一种二叉搜索树,每一个节点上有一个存储位表示节点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长上两倍,因而是接进…...

【百度Apollo】自动驾驶规划技术:实现安全高效的智能驾驶
🎬 鸽芷咕:个人主页 🔥 个人专栏:《linux深造日志》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! ⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下…...

《C程序设计》上机实验报告(五)之一维数组二维数组与字符数组
实验内容: 1.运行程序 #include <stdio.h> void main( ) { int i,j,iRow0,iCol0,m; int x[3][4]{{1,11,22,33},{2,28,98,38},{3,85,20,89}}; mx[0][0]; for(i0;i<3;i) for(j0;j<4;j) if (x[i][j]>m) { mx[i][j]; iRowi…...

【BUG】联想Y7000电池电量为0且无法充电解决方案汇总
因为最近火灾很多,所以昨天夜晚睡觉的时候把插线板电源关掉了,电脑也关机了。 各位一定要注意用电安全,网上的那些事情看着真的很难受qvq。 第二天早上起床的时候一看发现电脑直接没电了,插上电源后也是显示 你一定要冲进去啊(ू˃…...

centos7常用命令之安装插件2
centos7安装插件1 7、kibana 【启动kibana,需要调整这个配置文件(/opt/kibana-6.3.0/config/kibana.yml)的一处ip地址,因为每次虚拟机的ip地址可能会有所不同, 同时访问页面地址的ip:5601时,ip地址也对应修改】 1.解压缩包 cd /opt/ tar -xvf kibana-6.3.0-linux-x…...

MATLAB - 仿真单摆的周期性摆动
系列文章目录 前言 本例演示如何使用 Symbolic Math Toolbox™ 模拟单摆的运动。推导摆的运动方程,然后对小角度进行分析求解,对任意角度进行数值求解。 一、步骤 1:推导运动方程 摆是一个遵循微分方程的简单机械系统。摆最初静止在垂直位置…...

Pandas进阶--map映射,分组聚合和透视pivot_table详解
文章目录 1.Pandas的map映射(1)映射(2)map充当运算工具 2.数据分组和透视(1)分组统计 - groupby功能 是pandas最重要的功能(2)聚合agg 3.透视表pivot_table(1)…...

Visual Studio 和Clion配置Cocos2d-x环境
Visual Studio 和Clion配置Cocos2d-x环境 我就不贴图片的,懒得上传图床。懒。开发环境: 系统: Window11 编译器: CMake MSVC 开发工具:Clion or Visual Studio 请自行配置好,Python2.7,和Cmake Cocos2d-x下载…...

【百度Apollo】本地调试仿真:加速自动驾驶系统开发的利器
🎬 鸽芷咕:个人主页 🔥 个人专栏: 《linux深造日志》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! ⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下…...

ztest中ddof起什么作用
⭐️ statsmodels 中 ztest 基本使用 statsmodels 也是一个强大的统计分析库,提供了丰富的统计模型和检验功能。对于 Z 检验,statsmodels 提供了 ztest 函数。 以下是使用 statsmodels 进行 Z 检验的示例: from statsmodels.stats.weights…...

linux 主机无法联网问题
主机不能联网 一 查看当前ip ping路由 ifconfig wlan0 wlan0: flags4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 192.168.2.78 netmask 255.255.255.0 broadcast 192.168.2.255ping 192.168.2.1查看是否能ping通 二 查看路由表 route -n Destination G…...

2024/1/27 备战蓝桥杯 1-1
目录 求和 0求和 - 蓝桥云课 (lanqiao.cn) 成绩分析 0成绩分析 - 蓝桥云课 (lanqiao.cn) 合法日期 0合法日期 - 蓝桥云课 (lanqiao.cn) 时间加法 0时间加法 - 蓝桥云课 (lanqiao.cn) 扫雷 0扫雷 - 蓝桥云课 (lanqiao.cn) 大写 0大写 - 蓝桥云课 (lanqiao.cn) 标题…...

支持下一代网络IpV6的串口服务器,IpV6串口485接口转网口
和IPv4比较,IPv6有两个极具吸引力的特点:一个是IPv6采用的128位地址格式,而IPv4采用32位的地址格式,因此IPv6使地址空间增大了296;另一个是IPv6物联网数据业务具有更强的支持能力,成为未来物联网的重要协议…...

uniapp H5 实现上拉刷新 以及 下拉加载
uniapp H5 实现上拉刷新 以及 下拉加载 1. 先上图 下拉加载 2. 上代码 <script>import DragableList from "/components/dragable-list/dragable-list.vue";import {FridApi} from /api/warn.jsexport default {data() {return {tableList: [],loadingHi…...

网络工程师必学知识:2、IPv4和IPv6地址划分
网络工程师必学知识:2、IPv4和IPv6地址划分 1.概述:2.IPv4:地址划分:有类划分,无类划分。一、有类划分:分为5类。ABCDE,掩码分别位8、16、24、28、27取值范围:出类别bit不变…...

Rust - 变量
不管学什么语言好像都得从变量开始,不过只需要懂得大概就可以了。 但在Rust里不先把变量研究明白后面根本无法进行… 变量绑定 变量赋值❌ 变量绑定✔️ Rust中没有“赋值”一说,而是称为绑定。 int a 3; //C中的变量赋值 a 3; //python中的…...

【Linux】压缩脚本、报警脚本
一、压缩搅拌 要求: 写一个脚本,完成如下功能 传递一个参数给脚本,此参数为gzip、bzip2或者xz三者之一; (1) 如果参数1的值为gzip,则使用tar和gzip归档压缩/etc目录至/backups目录中,并命名为/backups/etc…...

用Flask打造一个大模型智能问答WEB网站
目前已经有很多类似GPT的大模型开源,可以提供类似ChatGPT的智能问答功能。我也基于这些开源模型,用Flask来建立一个智能问答网站,可以方便用户建立自己的ChatGPT系统。 这个网站需要提供用户登录功能,对已登录的用户,可以在网站上提出问题,并由大模型处理后返回答案。演…...

学习python第三天
一.数据类型 1.获取数据类型 x 10 print(type(x))""" 输出 <class int> """2.复数类型(complex)详解 复数(Complex)是 Python 的内置类型,直接书写即可。换句话说,…...

(M)UNITY三段攻击制作
三段攻击逻辑 基本逻辑: 人物点击攻击按钮进入攻击状态(bool isAttack) 在攻击状态下, 一旦设置的触发器(trigger attack)被触发,设置的计数器(int combo)查看目前攻击…...

PHP的线程安全与非线程安全模式选哪个
曾经初学PHP的时候也很困惑对线程安全与非线程安全模式这块环境的选择,也未能理解其中意。近来无意中看到一个教程对线程安全(饿汉式),非线程安全(懒汉式)的描述,虽然觉得现在已经能够很明了透彻…...

asdf安装不同版本的nodejs和yarn和pnpm
安装asdf 安装nodejs nodejs版本 目前项目中常用的是14、16和18 安装插件 asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git asdf plugin-add yarn https://github.com/twuni/asdf-yarn.git可以查看获取所有的nodejs版本 asdf list all nodejs有很多找…...

Spring的事件监听机制
这里写自定义目录标题 1. 概述(重点)2. ApplicationEventMulticaster2.1 SimpleApplicationEventMulticaster2.2 AbstractApplicationEventMulticaster 3. ApplicationListener3.1 注册监听器3.2 自定义 4. SpringApplicationRunListeners 1. 概述&#…...