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

优化自旋锁的实现

在《C++11实现一个自旋锁》介绍了分别使用TAS和CAS算法实现自旋锁的方案,以及它们的优缺点。TAS算法虽然实现简单,但是因为每次自旋时都要导致一场内存总线流量风暴,对全局系统影响很大,一般都要对它进行优化,以降低对全局系统的负面影响,本文讨论一下它的优化方案。

下面是使用atomic_flag原子类+TAS算法实现的自旋锁代码。

class spin_lock final {atomic_flag lock_var{false}; // 锁变量
public:void lock() {while (lock_var.test_and_set(memory_order_acquire));}void unlock() {lock_var.clear(memory_order_release);}
};

从上面的代码可以看到,在调用lock()函数申请锁时,只要锁变量lock_var的值不是false,就会无条件的循环执行test_and_set(),执行的太频繁了,以至于其它CPU的运行都要受到影响。因为一个CPU在test_and_set()执行时要原子事务写lock_var变量到内存中,每执行一次就导致一场内存总线流量风暴,其它CPU要访问内存就得竞争内存总线。如果竞争不到,得停下来等待内存总线的释放,造成CPU一定程度上处于“失速”状态。如果几个线程同时在申请锁,每一次TAS的原子事务写内存,都会加剧内存总线的竞争程度。

如何进行优化呢?

双检查机制

在实现单例模式时,我们知道有一种针对互斥锁的优化方案,叫做双检查锁机制,它的基本思想是先使用执行成本低的代码来检查条件是否满足,如果条件满足,再使用执行成本高的代码,即互斥锁的方式去检查条件。

可以借鉴这个思想来对TAS算法进行优化,基本思路是在调用TAS之前,先读取锁变量,此过程是只读内存操作,相比RMW操作是轻量级的操作,没有全局影响和副作用,执行成本较低。然后对读取的值进行判断,如果是true,则继续循环重复此过程,直到返回false,说明有线程已经释放锁了,此时退出循环,再进行重量级的TAS操作。也就是仅在必要的时候才执行TAS操作,这样,即使TAS操作导致的全局影响很大,但是它发生的次数却大为降低了。

因为C++11版的atomic_flag原子类没有提供读取原子变量的接口,直到C++20版本才提供了成员函数test(),它返回原子变量的值,可以使用这个接口来实现双检查操作。下面是修改后的lock()函数代码:

void lock() {while (true) {// 第一次检查:使用Test操作while (lock_var.test(memory_order_relaxed));// 第二次检查并设置:TAS操作if (!lock_var.test_and_set(memory_order_acquire)) {break; // 成功获得锁,跳出循环}}
}

与前面的实现相比,多了一个test()循环调用,同时原来的test_and_set()循环调用变成了单次调用,只有在test()检测到lock_var为false时退出循环之后,才会进行test_and_set()调用。

循环代码“while (lock_var.test(memory_order_relaxed))”就是上面说的第一次检查操作,调用atomic_flag::test()接口返回lock_var的值,这里没有内存顺序的需要,因此使用了最松散的内存序,如果返回值是true,则继续循环,如果是false,则退出循环,立刻进入第二次检查,这时为什么还要进行检查呢?因为第一次检查不能原子地设置lock_var为true,所以还得要使用TAS操作,由于已经检测到lock_var为false了,这次执行TAS成功的几率非常高。

由此可见,该方式在自旋锁中的实现逻辑是Test-Test-And-Set,所以也简称为TTAS算法。

为了查看TTAS的实现细节,我们看一下它的汇编指令代码,下面是使用-std=c++20编译选项,生成的汇编指令:

spin_lock::lock():mov     edx, 1
.L17:
// 从内存中读取锁变量的值movzx   eax, BYTE PTR [rdi]
// 判断是否是0,即false,第一次检查 Testtest    al, al
// 如果不是0,则回退jne     .L17mov     eax, edx
// 第二次检查并设置:TAS操作xchg    al, BYTE PTR [rdi]test    al, aljne     .L17ret

看第一次检查(第3-9行),在每次检查时都要从内存[rdi]处读取变量lock_var,然后检查是否为0,即false,如果不为0,则继续退回去重新读取再检查,一直循环执行“读取-判断-回退”的逻辑,直到读出的lock_var为0为止。注意,这里的读取指令是“movzx eax, BYTE PTR [rdi]”,是非常简单的指令,它从内存中加载一个字节的数据到寄存器al中,也是一条原子操作指令,但该指令不会锁总线。根据MESI缓存一致性协议,如果lock_var的值没有被别的CPU修改过,那么它就在当前CPU的L1 cache缓存中会一直有效,也就是只要内存中的lock_var的值没有被更新,该指令执行时就会一直从L1 cache中读取,即没有访问内存,也就不会占用内存总线,不会影响其它CPU,只在本CPU中循环,所以第一次的循环检查也被称为“本地自旋”(见汇编指令代码第5(读取)、7(判断)、9(回退)行)。

1、提高了整体吞吐量性能

同TAS算法相比,TTAS算法大大减轻了对内存总线的竞争程度,也降低了TAS执行时为了维持cache一致性而产生的内存总线流量。使用《利用C++11原子操作实现自旋锁》中的测试代码,所测试的TTAS以及TAS、CAS算法的性能数据如下:

TTAS所用时钟周期        TAS所用时钟周期      CAS所用时钟周期
337427552              432447222           177915346
394388223              502814284           279384821
390494013              556108365           227520077
401288104              513192991           346827330

可见,在同样的测试条件,TTAS算法要比TAS算法性能要好,但是仍然比CAS算法要差。

2、增大了个体CPU申请锁时延迟的可能性

TAS算法实现自旋锁时,如果返回了false同时也就获得了锁,检查和设置是一个原子操作,而使用TTAS算法时要对锁变量进行两次检查判断,第一次检查返回false时,只是有可能会获得锁,能否获得还得要使用后面的TAS算法。因此,使用TTAS算法实现,申请锁的速度没有TAS算法快,也就是申请锁时的延迟要比TAS算法大。

因此,可以把Test和TAS的位置调换一下,先进行TAS算法,当一个线程刚进入lock()时,如果锁是释放状态,可能会马上通过TAS来获得锁,而不用等到从Test循环中退出,再使用TAS来竞争锁,能降低一点申请锁的延迟,当然如果第一次没有竞争到锁,就进入了本地自旋,等待lock_var变为false,仍然还是存在延迟。代码如下:

void lock() {while (true) {// 第一次检查并设置:TAS操作if (!lock_var.test_and_set(memory_order_acquire)) {break; // 成功获得锁,跳出循环}// 第二次检查:使用Test操作while (lock_var.test(memory_order_relaxed));}
}

3、TTAS是比TAS更“忙”的算法

在TTAS算法中,第一个Test检查过程是本地自旋,它不同于TAS自旋,它的性能更高,执行的速度更快:

首先,先看指令部分,是个微小循环,可以被CPU的循环流检测器(识别和锁定微操作指令队列中小的循环)检测到,并放入trace-cache中,可以在下一次循环时直接使用。下图是它的指令流截图,非常短小精悍,仅有三行指令,指令长度是17-10=7个字节度,被译码单元译码后的微操作码应该也不多(其中jne指令和它前面的test指令还会被宏融合为单一的微操作码),它们存放在CPU的trace-cache里面肯定绰绰有余,注意里面存放的是已经解码完的微操作码,可以被执行引擎直接派发和执行。也就是说对于这个本地自旋小循环,不但不需要从指令cache中获取指令,而且连指令解码过程也被省掉了,此时不会因为程序流程发生跳转了,需要重新从指令cache中加载指令和译码的过程,导致CPU出现流水线的中断。

图片

再看数据部分,因为对锁变量数据lock_var是一个只读操作,此时作为热点数据都在L1 cache中,读取时不需要从内存中获取,速度极快,因此,在本地自旋时,也不会出现因为需要加载数据导致CPU流水线中断的情况。此外,因为这个本地自旋运行次数很多,CPU的分支预测单元会发挥作用,知道下一步要跳转到第10行去执行,CPU就会预测执行,这样这三条指令就可以并行执行,即test指令在判断当前lock_ver值的同时,又在读取下一次的lock_var值为下一次test指令做准备,这是指令级的并行化(因为有寄存器重命名机制,第10行更新eax(al是它的低8位)寄存器和第13行测试al寄存器不会冲突,这个写后读是假依赖,可以同时执行)。

也就是说,当CPU处于本地自旋时,无论是指令还是数据,它们都处于CPU的内部,不会发生因为cache miss而中断CPU流水线去等待它们,而且还可以进行指令级的并行化,会一直处于高速运转中,也就是它更“忙”了,当然副作用就是耗电量非常大,这是TTAS算法不好的一面。

4、一旦进入本地自旋,大概率是要空转

再看TTAS算法的代码实现,当第一次检查时,如果一个CPU从本地自旋中退出,说明此时已经有别的CPU把这个lock_var变量置为false了,即别的线程已经把锁释放了。当接着使用TAS操作进行第二次检查时,如果没有设置成功,说明在第一次检查结束和第二次检查开始之间出现了状况:第一种可能它是和别的CPU在执行时间上有重叠,但别的CPU抢先获得了锁;第二种可能是执行流程被中断后又返回了,即有中断或者优先级更高的线程把它抢占了,这个CPU也就暂时没有参与自旋锁的竞争。

我们现在讨论第一种情况,别的CPU抢先获得了锁,然后进入临界区进行业务处理,这也暗示着自旋锁不会马上被释放,那么本CPU肯定会再次进入“本地自旋”并且会持续一段时间。我们不妨估算一下大概需要自旋多长时间:如果想让CPU从本地自旋中退出,至少要等到另一个CPU执行完临界区代码后,调用unlock把false值写入内存变量lock_var,本CPU读取lock_var变量时发现缓存和内存中的值不一致,重新从内存中加载;假设临界区的最小执行时间为t,也就是说最少也要等待CPU一次原子事务写内存的时间,加上一次读内存的时间,再加上t,小于这个时间是不会从循环中退出的,这段时间CPU一直处于空转状态。

也就是说,只要进入本地自旋,大概率是要一直空转的,一直到别的线程执行完临界区代码释放锁后才有可能停止空转。那么,这个空转既然避免不了,在这期间CPU有什么优化措施吗?

x86的pause指令

现代CPU大多提供了超线程技术(即逻辑CPU),即在一个物理核上有两个逻辑核,它们有自己独享的寄存器,但是需要共享执行单元、系统总线和缓存等硬件资源。如果执行自旋操作的CPU是一个逻辑核,当它进入本地自旋的时候,会加剧物理核中的执行资源和系统总线的竞争程度,从而影响其它同一个逻辑核的执行效率。

我们再分析一下TTAS的lock代码的结构特点:代码虽然不多,但是严重同质化,有两次数据传输操作,两次test操作,一次比较交换操作,也就是它们使用相同执行单元的可能性非常大。如果处于自旋的两个线程刚好被分配到同一个物理核的两个逻辑核上运行,两个逻辑核共享执行单元和系统总线,它们在自旋操作时可能会遭遇资源冲突,反而导致整体性能下降。

为了改善这种情况,Intel开发者手册建议使用pause指令优化。intel的CPU针对自旋等待的场景提供了一个指令:pause,该条指令的功能是给CPU一个提示:当前正在进行自旋等待,可以暂停一下,比如等待一个或几个内存总线操作周期的时间之后,然后再继续运行(因为在这期间,CPU是大概率获取不到自旋锁的,与其让CPU自旋忙等,不如停下来休息一下)。因此,一个逻辑核进入了pause状态,也就不再使用系统资源了,这样,同物理核的另一个逻辑核就可以独享物理核的全部资源了,提高了申请锁时响应速度,也降低了CPU的电量消耗。

因为C++中没有相应的函数来执行pause指令,可以在代码中嵌入汇编指令,下面是基于TTAS算法修改后的代码(同样也可以使用pause指令优化基于CAS算法实现的自旋锁):

void lock() {while (true) {if (!lock_var.test_and_set(memory_order_acquire)) {break;}while (lock_var.test(memory_order_relaxed)) {asm("pause");}}
}

这情况下能够大大降低CPU执行资源和电量的消耗。不过,这种方法会进一步增加获取锁的延迟性,因为每次自旋条件不符合时就得先pause一段时间,如果在pause期间,锁被释放了,也不会及时的获取锁,只能等到CPU从pause中退出时才有可能。而且这个延迟的时间也不固定:有可能CPU刚刚开始pause,锁就释放了,显然此时延迟最大;也有可能CPU从pause退出时,恰好锁就释放了,显然此时延迟最小,这是最理想的场景。如果应用场景是对响应性能敏感的场景,就得要慎重考虑了,能否容忍使用pause指令带来的延迟损失。

同时我们也知道,当CPU处于pause状态时,什么也不做,不像操作系统提供的yield命令,可以让CPU执行别的任务。让CPU资源处于闲置状态,可能会感觉有点可惜,不过在支持超线程技术的CPU架构体系中可以利用这个特点,在一定程度上缓解逻辑核的资源竞争问题,提高超线程的执行速度,因为一个逻辑核执行pause而处于暂停状态,也在一定程度上缓解了CPU的耗电问题。

不过需要指出的是,当自旋锁保护的临界区执行时间很短时,比如只是简单的几个内存访问和计算,可以使用pause机制;如果自旋锁保护的临界区执行时间很长时,或者一个线程获得自旋锁后接着被抢占了,使用pause机制会让其它申请自旋锁的CPU长时间处于暂停状态,也是一种资源浪费。显然这种情况下不如把CPU分配给别的线程使用,此时涉及到线程被调度的情况了,就得考虑让操作系统参与进来了,比如调用yield,把CPU调度给别的任务,或者直接把当前线程挂起,等到解锁时再唤醒它。

总结

1、TAS算法实现自旋锁,会导致内存总线流量风暴,全局系统影响大。

2、TTAS虽然抑制了流量风暴的产生,减轻了全局内存总线的竞争程度,但是又导致CPU耗电量大、发热等情况。

3、使用pause指令缓解了超线程核心的系统资源竞争和降低了耗电量,但是又增长了获取锁时的延迟。

4、TTAS算法和pause指令是在程序和CPU上面对自旋锁的优化,如果获取不到锁时,线程仍然处于自旋中,不会发生调度和阻塞现象,没有改变自旋锁的本质特征。

参考:

1、https://zh.cppreference.com/w/cpp/atomic/atomic_flag/test

2、Intel® 64 and IA-32 Architectures Software Developer’s Manual

3、《现代x86汇编语言程序设计》 丹尼尔-卡斯沃姆

附录:

下面内容是Intel手册对pause指令的描述。

PAUSE—Spin Loop Hint
Improves the performance of spin-wait loops. When executing a “spin-wait loop,” processors will suffer a severe performance penalty when exiting the loop because it detects a possible memory order violation. The PAUSE instruction provides a hint to the processor that the code sequence is a spin-wait loop. The processor uses this hint to avoid the memory order violation in most situations, which greatly improves processor performance. For this reason, it is recommended that a PAUSE instruction be placed in all spin-wait loops.

An additional function of the PAUSE instruction is to reduce the power consumed by a processor while executing a spin loop. A processor can execute a spin-wait loop extremely quickly, causing the processor to consume a lot of power while it waits for the resource it is spinning on to become available. Inserting a pause instruction in a spinwait loop greatly reduces the processor’s power consumption.

相关文章:

优化自旋锁的实现

在《C11实现一个自旋锁》介绍了分别使用TAS和CAS算法实现自旋锁的方案,以及它们的优缺点。TAS算法虽然实现简单,但是因为每次自旋时都要导致一场内存总线流量风暴,对全局系统影响很大,一般都要对它进行优化,以降低对全…...

项目实战--新闻分类

从antd中拿一个表格 表格 Table - Ant Designhttps://ant-design.antgroup.com/components/table-cn#table-demo-edit-cell使用的是可编辑单元格 实现引入可编辑单元格: import React, { useState, useEffect, useRef, useContext } from react import { Button, …...

人像面部关键点检测

此工作为本人近期做人脸情绪识别,CBAM模块前是否能加人脸关键点检测而做的尝试。由于创新点不是在于检测点的标注,而是CBAM的改进,因此,只是借用了现成库Dilb与cv2进行。 首先,下载人脸关键点预测模型:Index of /file…...

OpenVINO怎么用

目录 OpenVINO 简介 主要组件 安装 OpenVINO 使用 OpenVINO 的基本步骤 OpenVINO 简介 OpenVINO(Open Visual Inference and Neural Network Optimization)是英特尔推出的一个开源工具包,旨在帮助开发者在英特尔硬件平台上高效部署深度学…...

写论文时降AIGC和降重的一些注意事项

‘ 写一些研究成果,英文不是很好,用有道翻译过来句子很简单,句型很单一。那么你会考虑用ai吗? 如果语句太正式,高级,会被误判成aigc ,慎重选择ai润色。 有的话就算没有用ai生成,但…...

SpringBoot学习(properties、yml(主流)、yaml格式配置文件)(读取yml配置文件的3种方式)(详解)

目录 一、SpringBoot配置文件详解。 1.1配置文件简介。 1.2配置文件分类。&#xff08;3种配置文件格式&#xff09; <1>application.properties&#xff08;properties格式&#xff09;。 <2>application.yml&#xff08;yml格式&#xff09;。 <3>applicat…...

STM32单片机C语言

1、stdint.h简介 stdint.h 是从 C99 中引进的一个标准 C 库的文件 路径&#xff1a;D:\MDK5.34\ARM\ARMCC\include 大家都统一使用一样的标准&#xff0c;这样方便移植 配置MDK支持C99 位操作 如何给寄存器某个值赋值 举个例子&#xff1a;uint32_t temp 0; 宏定义 带参…...

前端为什么需要单元测试?

一. 前言 对于现在的前端工程&#xff0c;一个标准完整的项目&#xff0c;通常情况单元测试是非常必要的。但很多时候我们只是完成了项目而忽略了项目测试。我认为其中一个很大的原因是很多人对单元测试认知不够&#xff0c;因此我写了这篇文章&#xff0c;一方面期望通过这篇…...

QT 文件和文件夹操作

文件操作 1. 文件读写 QFile - 基本文件操作 // 只写模式创建文件&#xff08;如果文件已存在会清空内容&#xff09; file.open(QIODevice::WriteOnly);// 读写模式创建文件 file.open(QIODevice::ReadWrite);// 追加模式&#xff08;如果文件不存在则创建&#xff09; fil…...

AIP目录

专注于开发灵活API的设计文档。 AIP是总结了谷歌API设计决策的设计文档&#xff0c;它也为其他人提供了用文档记录API设计规则和实践的框架和系统。 基础1AIP目的和指南2AIP编号规则3AIP版本管理200先例8AIP风格与指导9术语表流程100API设计评审常见问题205Beta版本发布前置条…...

Function Calling的时序图(含示例)

&#x1f9cd; 用户&#xff1a; 发起请求&#xff0c;输入 prompt&#xff08;比如&#xff1a;“请告诉我北京的天气”&#xff09;。 &#x1f7ea; 应用&#xff1a; 将用户输入的 prompt 和函数定义&#xff08;包括函数名、参数结构等&#xff09;一起发给 OpenAI。 …...

基于尚硅谷FreeRTOS视频笔记——6—滴答时钟—上下文切换

FreeRTOS滴答 FreeRTOS需要有一个时钟参照&#xff0c;并且这个时钟不会被轻易打断&#xff0c;所以最好选择systick 为什么需要时间参照 就是在高优先级任务进入阻塞态后&#xff0c;也可以理解为进入delay&#xff08;&#xff09;函数后&#xff0c;需要有一个时间参照&…...

Playwright框架入门

Playwright爬虫框架入门 Playwright介绍 playwright官方文档 Playwright是一个用于自动化浏览器操作的开源工具&#xff0c;由Microsoft开发和维护&#xff0c;支持多种浏览器和多种编程语言&#xff0c;可以用于测试、爬虫、自动化任务等场景。 Playwright是基于WebSocket…...

针对渲染圆柱体出现“麻花“状问题解决

圆柱体渲染结果&#xff0c;在侧面有麻花状条纹&#xff0c;边缘不够硬朗&#xff0c;上下的圆看起来不够平&#xff0c;很明显&#xff0c;是法向量导致的。 原始模型 渲染结果 计算点的法向量采用简单的平均法&#xf…...

手撕数据结构算法OJ——栈和队列

文章目录 一、前言二、手撕OJ2.1有效的括号2.2用队列实现栈2.2.1初始化2.2.2入栈2.2.3出栈2.2.4取栈顶2.2.5判空2.2.6销毁2.2.7整体代码 2.3用栈实现队列2.3.1初始化2.3.2入队2.3.3出队2.3.4取队头2.3.5判空2.3.6销毁2.3.7整体代码 四、总结 一、前言 兄弟们&#xff0c;今天的…...

基础知识-指针

1、指针的基本概念 1.1 什么是指针 1.1.1 指针的定义 指针是一种特殊的变量&#xff0c;与普通变量存储具体数据不同&#xff0c;它存储的是内存地址。在计算机程序运行时&#xff0c;数据都被存放在内存中&#xff0c;而指针就像是指向这些数据存放位置的 “路标”。通过指针…...

Thymeleaf简介

在Java中&#xff0c;模板引擎可以帮助生成文本输出。常见的模板引擎包括FreeMarker、Velocity和Thymeleaf等 Thymeleaf是一个适用于Web和独立环境的现代服务器端Java模板引擎。 Thymeleaf 和 JSP比较&#xff1a; Thymeleaf目前所作的工作和JSP有相似之处&#xff0c;Thyme…...

ifconfig -bash: ifconfig: command not found

Ubuntu系统安装完成想查看其ip 报错ifconfig -bash: ifconfig: command not found 解决方法 sudo apt update sudo apt install net-tools ip查找成功...

MCP协议量子加密实践:基于QKD的下一代安全通信(2025深度解析版)

一、量子计算威胁的范式转移与MCP协议改造必要性 1.1 传统加密体系的崩塌时间表 根据IBM 2025年量子威胁评估报告&#xff0c;当量子计算机达到4000个逻辑量子比特时&#xff08;预计2028年实现&#xff09;&#xff0c;现有非对称加密体系将在72小时内被完全破解。工业物联网…...

STM32 基本GPIO控制

目录 GPIO基础知识 ​编辑IO八种工作模式 固件库实现LED点灯 蜂鸣器 按键基础知识 ​编辑继电器 震动传感器 433M无线模块 GPIO基础知识 GPIO(General-Purpose input/output,通用输入/输出接口) 用于感知外部信号&#xff08;输入模式&#xff09;和控制外部设备&…...

【天外之物】叉乘(向量积)的行列式表示方法

叉乘&#xff08;向量积&#xff09;的行列式表示方法如下&#xff1a; 步骤说明&#xff1a; 构造33矩阵&#xff1a; 将三维向量叉乘转换为行列式的形式&#xff0c;需构造一个包含单位向量 i , j , k \mathbf{i}, \mathbf{j}, \mathbf{k} i,j,k 和原向量分量的矩阵&#x…...

北京SMT贴片厂精密制造关键工艺

内容概要 随着电子设备小型化与功能集成化需求日益提升&#xff0c;北京SMT贴片厂在精密制造领域持续突破工艺瓶颈。本文以高密度PCB板贴片全流程为核心&#xff0c;系统梳理从锡膏印刷、元件贴装到回流焊接的关键技术节点&#xff0c;并结合自动化检测与缺陷预防方案&#xf…...

服务器架构:SMP、NUMA、MPP及Docker优化指南

文章目录 引言 一、服务器架构基础1. SMP&#xff08;对称多处理&#xff0c;Symmetric Multiprocessing&#xff09;2. NUMA&#xff08;非统一内存访问&#xff0c;Non-Uniform Memory Access&#xff09;3. MPP&#xff08;大规模并行处理&#xff0c;Massively Parallel Pr…...

Datawhale春训营赛题分析和总结

1.Datawhale春训营任务一 借助这个云平台&#xff0c;支持类似于这个anaconda相关的交互式的操作&#xff0c;第一个任务就是跑通这个baseline&#xff0c;然后注册账号之后送了对应的相关算力&#xff0c;跑通这个之后需要进行打卡&#xff0c;跑通其实是没问题不大的&#x…...

一键模仿图片风格,图生生APP,实现随时随地“生图自由“

一、什么是"图片模仿"功能&#xff1f; "图片模仿"是图生生AI的功能之一&#xff0c;利用先进的AI技术&#xff0c;分析上传的图片风格、色调、构图等元素&#xff0c;快速生成具有相同风格的图片。无论是产品展示、广告海报还是社交媒体配图&#xff0c;…...

C++——C++11常用语法总结

C11标准由国际标准化组织&#xff08;ISO&#xff09;和国际电工委员会&#xff08;IEC&#xff09;旗下的C标准委员会&#xff08;ISO/IEC JTC1/SC22/WG21&#xff09;于2011年8月12日公布&#xff0c;并于2011年9月出版。2012年2月28日的国际标准草案(N3376)是最接近于C11标准…...

C++17 新特性简解

C17 新特性简解 一、核心语言特性 1. 结构化绑定&#xff08;Structured Bindings&#xff09; 用途&#xff1a;解构复合类型&#xff08;如元组、结构体&#xff09;为独立变量 示例&#xff1a; #include <iostream> #include <tuple>int main() {// 解构 st…...

【失败】Gnome将默认终端设置为 Kitty

起因 一会儿gnome-terminal一会儿kitty终端&#xff0c;实在是受不了&#xff0c;决定取缔默认的gnome-terminal。 过程 在 Ubuntu 或 Debian 系统上&#xff1a; 确保 Kitty 已经安装。如果未安装&#xff0c;可以在终端中运行命令sudo apt install kitty -y进行安装。 使用系…...

【Easylive】微服务架构在系统中的优缺点的具体体现

【Easylive】项目常见问题解答&#xff08;自用&持续更新中…&#xff09; 汇总版 在线视频分享系统项目简介 系统概述 该项目是一个基于SpringCloud微服务架构的在线视频分享系统&#xff0c;主要功能包括&#xff1a; • 用户自主发布视频 • 后台视频审核 • 用户互动…...

锁(Mutex)、信号量(Semaphore)与条件量(Condition Variable)

一、同步机制的核心意义 在多线程/多进程编程中&#xff0c;当多个执行流共享资源&#xff08;如变量、内存、文件&#xff09;时&#xff0c;可能因操作顺序不确定导致数据竞争&#xff08;Data Race&#xff09;。同步机制的作用是&#xff1a; 保证原子性&#xff1a;确保…...

使用pnpm第一次运行项目报错 ERR_PNPM_NO_PKG_MANIFEST No package.json found in E:\

开始用unibestpnpm写一个小程序 运行pnpm init报错 如标题所示没有package.json这个文件 博主犯了一个很愚蠢的错误。。 准备方案手动创建一个json文件 此时才发现没到根目录下&#xff0c;创建了一个项目之后就没有切入文件夹里。 切入根目录再下载就成功啦...

前沿篇|CAN XL 与 TSN 深度解读

引言 1. CAN XL 标准演进与设计目标 2. CAN XL 物理层与帧格式详解 3. 时间敏感网络 (TSN) 关键技术解析 4. CAN XL + TSN 在自动驾驶领域的典型应用...

从零开始学A2A一:A2A 协议的高级应用与优化

A2A 协议的高级应用与优化 学习目标 掌握 A2A 高级功能 理解多用户支持机制掌握长期任务管理方法学习服务性能优化技巧 理解与 MCP 的差异 分析多智能体场景下的优势掌握不同场景的选择策略 第一部分&#xff1a;多用户支持机制 1. 用户隔离架构 #mermaid-svg-Awx5UVYtqOF…...

追赶地球变化的“快镜头“:遥感时间分辨率的奥秘

在地球表面&#xff0c;万物的生长、兴衰和变迁&#xff0c;如同一部永不停歇的电影。而科学家们想要看清每一帧画面&#xff0c;就必须依赖遥感卫星这个"超级摄影师"。今天&#xff0c;我们就来聊聊遥感领域里一个关键的维度——时间分辨率。 想象一下&#xff0c;如…...

轻量还是全量?Kubernetes ConfigMap 与专业配置中心的抉择

文章目录 简介什么是 ConfigMapConfigMap 的核心能力配置存储与注入动态更新与 Kubernetes 原生生态深度集成 ConfigMap 的固有局限专业配置中心对比选型建议结语 简介 在现代微服务架构中&#xff0c;集中式配置管理是保证应用可维护性、可扩展性和安全性的关键环节。Kuberne…...

mybatis plus打印sql日志到指定目录

1、mybatis plus打印sql日志 参考文档&#xff1a;mybatis plus打印sql日志_mybatisplus日志打印-CSDN博客 2、修改 修改InfoLevelLogger Override public void debug(String s) {// 修改这里logger.info(s);log.debug(s); } 增加&#xff1a;log.debug(s); 修改logback.x…...

CUDAfy的使用

此文章是对一篇使用文档的翻译总结 文档地址 https://www.doczj.com/doc/0f2570173.html CUDAfy使用步骤​ 1. 环境准备​ 硬件要求​​&#xff1a;支持CUDA的NVIDIA显卡&#xff08;如GTX系列&#xff09;。​​软件安装​​&#xff1a; 安装最新版NVIDIA驱动。下载并安…...

DeepSeek 部署中的常见问题及解决方案

DeepSeek(深度求索)大模型部署过程中常见问题的系统性解决方案,涵盖环境配置、模型加载、性能优化、安全防护及企业级部署等核心场景,结合官方文档与社区实践提供可落地的操作指南: 一、环境配置与依赖问题 1. 操作系统与硬件兼容性 问题表现:部署失败提示驱动不兼容或…...

【AI提示词】儿童看护员

提示说明 儿童看护员旨在帮助用户构建一个既专业又富有爱心的儿童看护员角色&#xff0c;以满足儿童在成长过程中的各种需求。 提示词 # 角色 儿童看护员## 注意 1. 儿童看护员应具备亲和力和耐心&#xff0c;以确保与儿童的良好互动。 2. 专家设计应关注儿童的安全、健康和…...

深入解析 Python 中的装饰器 —— 从基础到实战

1. 装饰器基础 1.1 什么是装饰器&#xff1f; 装饰器本质上是一个 Python 函数&#xff0c;它可以让其他函数在不需要做任何代码变动的前提下增加额外功能。装饰器的返回值也是一个函数对象。 1.2 语法糖&#xff1a; 符号 Python 提供了 符号作为装饰器的语法糖&#xff0c…...

学习海康VisionMaster之中线查找

一&#xff1a;进一步学习了 今天学习下VisionMaster中的中线查找&#xff0c;这个就是字面意思&#xff0c;输入两条直线&#xff0c;输出两条直线的中线 二&#xff1a;开始学习 1&#xff1a;什么是中线查找&#xff1f;今天这个比较简单&#xff0c;其实这个模块算是一个几…...

笔记整理五

STP生成树 stp生成树是用于解决二层环路问题的协议。 二层环路为有以下三种&#xff1a; 1.广播风暴 2.MAC地址的偏移&#xff08;每一次循环&#xff0c;都会导致交换机来回刷新MAC地址表记录&#xff09; 3.多帧复制 stp生成树&#xff1a;需要将原本的环型拓扑结构转换…...

js实现的前端水印

效果 var defaultConfig {content: [], // 水印内容数组fontSize: 14, // 字体大小(px)fontFamily: sans-serif, // 字体color: rgba(255, 255, 255,.3), // 字体颜色rotate: -20, // 旋转角度(度数)zIndex: 2147483647, // 层级globalAlpha: 0.5, // 透明度canvasWidth: 30…...

前端服务器部署报错记录

报错1&#xff1a;Refused to apply style from http://xxxxxxx.online/assets/index.DepkYCYv.css because its MIME type (text/plain) is not a supported stylesheet MIME type, and strict MIME checking is enabled. index-Dnq3oQAv.js:1 解释&#xff1a;浏览器期望加载…...

JAVA中多线程的经典案例

文章目录 一、线程安全的单例模式&#xff08;一&#xff09;饿汉模式&#xff08;二&#xff09;懒汉模式 二、阻塞队列&#xff08;一&#xff09;生产者消费者模型&#xff08;二&#xff09;阻塞队列&#xff08;三&#xff09;自定义阻塞队列 三、定时器&#xff08;一&am…...

国产三维CAD皇冠CAD(CrownCAD)在「轨道交通行业」建模教程:轨道列车

在轨道交通行业&#xff0c;复杂系统集成、大规模装配验证与跨地域协同设计始终是核心痛点。传统设计工具难以应对动辄百万级零部件的装配挑战&#xff0c;且数据孤岛、版本混乱、硬件成本高昂等问题长期制约行业数字化转型。皇冠CAD&#xff08;CrownCAD&#xff09;作为国产云…...

Linux 日常运维命令大全

Linux 作为一种开源操作系统&#xff0c;在服务器运维中扮演着重要角色。掌握常用的 Linux 命令对于运维人员而言至关重要。本文将整理一份 Linux 服务器运维常用命令大全&#xff0c;帮助你在日常工作中提高效率和准确性。 1. 基础命令 基础命令是Linux操作的起点&#xff0…...

安全测试报告模板

安全测试报告 一、项目概况 项目名称XX智慧医疗平台被测系统版本V2.3.1测试类型渗透测试漏洞扫描测试时间2024年2月15-20日测试标准OWASP TOP 10 2021 二、测试环境 生产环境镜像&#xff1a; - 服务器&#xff1a;CentOS 7.9 Tomcat 9 - 数据库&#xff1a;MySQL 8.0集群…...

树莓派超全系列教程文档--(31)config.txt常用选项介绍

config.txt常用选项介绍 常用选项常用显示选项hdmi_enable_4kp60 常用硬件配置选项camera_auto_detectdisplay_auto_detectdtoverlaydtparamarm_boostpower_force_3v3_pwm 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 常用选项 常用显示选项 …...

算法-堆+单调栈

堆 首先堆在我们的Java中我们的是一个优先队列类 PriorityQueue 然后我们要弄最大堆和最小堆 最大堆&#xff1a; PriorityQueue<Integer> pq new PriorityQueue<Integer>((a, b) -> b - a); 最小堆&#xff1a; PriorityQueue<Integer> pq new P…...