[前端]异步请求的竞态问题
竞态条件简介
遇到的问题
切换标签请求数据,但又快速切换标签请求数据,展示的是前一个标签的数据,
需要在切换标签时添加取消请求的机制
,使用AbortController
来取消正在进行的请求。当用户快速切换标签时,取消之前的请求,只保留最新的请求
。同时需要优化状态管理,确保在切换标签时正确重置状态。
-
添加了 AbortController 来管理异步请求的生命周期
:- 使用 useRef 保存当前请求的 AbortController 实例
- 在每次发起新请求前,会先取消之前的请求
- 请求被取消时会清理相关的定时器和状态
-
优化了 loadMoreBlogs 函数的错误处理
:- 使用
try/catch 结构来处理异步操作
- 区分
请求取消和其他错误类型
- 在
finally
块中确保只有在请求未被取消时才更新状态
- 使用
-
增强了 useEffect 的清理机制:
- 在
组件卸载或依赖项改变时自动取消进行中的请求
重置 AbortController 实例
- 在
这些改进确保了:
- 当用户快速切换标签时,
旧的请求会被立即取消
只有最新的请求结果会被显示
- 避免了状态更新的
竞态条件
- 防止了
内存泄漏
import { Typography, Image, Spin } from 'antd';
import { useEffect, useRef, useState } from 'react';
import '../../assets/css/blogs.css';
import { generateBlogImage } from './utils';const { Title } = Typography;import blogData from './mock';interface BlogItem {id: number;image: string;title: string;theme: string;abstract: string;url: string;
}const Blogs = () => {const [blogs, setBlogs] = useState<BlogItem[]>([]);const [loading, setLoading] = useState(false);const [currentPage, setCurrentPage] = useState(1);const [selectedCategory, setSelectedCategory] = useState('全部');const [hasMore, setHasMore] = useState(true);const blogsGridRef = useRef<HTMLDivElement>(null);const abortControllerRef = useRef<AbortController | null>(null);// 从mock数据中获取博客数据const mockBlogs = (page: number): BlogItem[] => {const filteredData = selectedCategory === '全部' ? blogData : blogData.filter(item => item.theme === selectedCategory);const pageSize = 6;const start = (page - 1) * pageSize;const end = start + pageSize;const pageData = filteredData.slice(start, end);return pageData.map((blog, index) => ({id: Date.now() + index,image: generateBlogImage(blog),title: blog.title,theme: blog.theme,abstract: blog.title,url: blog.url}));};// 加载更多博客const loadMoreBlogs = async () => {if (!hasMore) return; // 取消之前的请求if (abortControllerRef.current) {abortControllerRef.current.abort();abortControllerRef.current = null;}// 创建新的 AbortControllerconst abortController = new AbortController();abortControllerRef.current = abortController;let isCancelled = false;const currentCategory = selectedCategory;setLoading(true);try {// 使用 signal 来控制请求的取消await new Promise((resolve, reject) => {const timeoutId = setTimeout(resolve, 1000);const handleAbort = () => {clearTimeout(timeoutId);isCancelled = true;reject(new Error('Request aborted'));};abortController.signal.addEventListener('abort', handleAbort);});// 检查分类是否已改变或请求已被取消if (currentCategory !== selectedCategory || isCancelled || abortController.signal.aborted) {setLoading(false);return;}const newBlogs = mockBlogs(currentPage);// 再次检查分类是否已改变或请求已被取消if (currentCategory !== selectedCategory || isCancelled || abortController.signal.aborted) {setLoading(false);return;}if (newBlogs.length === 0) {setHasMore(false);} else {setBlogs(prev => [...prev, ...newBlogs]);setCurrentPage(prev => prev + 1);}} catch (error) {if (!isCancelled && error.message !== 'Request aborted') {console.error('加载博客失败:', error);}} finally {if (!isCancelled && !abortController.signal.aborted) {setLoading(false);abortControllerRef.current = null;}}};// 初始加载useEffect(() => {setBlogs([]);setCurrentPage(1);setHasMore(true);loadMoreBlogs();// 清理函数:组件卸载或依赖项改变时取消正在进行的请求return () => {if (abortControllerRef.current) {abortControllerRef.current.abort();abortControllerRef.current = null;}};}, [selectedCategory]);// 滚动加载更多(接近底部 1/5 时)useEffect(() => {const handleScroll = () => {const container = blogsGridRef.current;if (!container || loading || !hasMore) return;const { scrollTop, scrollHeight, clientHeight } = container;// 到达底部 1/5 范围内if (scrollHeight - scrollTop - clientHeight < clientHeight / 5) {loadMoreBlogs();}};const container = blogsGridRef.current;if (container) {container.addEventListener('scroll', handleScroll);}return () => {if (container) {container.removeEventListener('scroll', handleScroll);}};}, [loading, hasMore]);const categories = ['全部', '前端','React', 'Vue', 'WebGL', 'GIS','工程化', '性能优化', '原理', '前沿'];const [scrollPosition, setScrollPosition] = useState(0);// 窗口大小变化时更新滚动位置useEffect(() => {const handleResize = () => {const container = categoryFilterRef.current;if (container) {setScrollPosition(container.scrollLeft);}};window.addEventListener('resize', handleResize);handleResize(); // 初始调用return () => window.removeEventListener('resize', handleResize);}, [scrollPosition, window.innerWidth]); // 添加window.innerWidth依赖const categoryFilterRef = useRef<HTMLDivElement>(null);const handleScroll = (direction: 'left' | 'right') => {const container = categoryFilterRef.current;if (!container) return;const scrollAmount = 200; // 每次滚动的距离const newPosition = direction === 'left' ? Math.max(0, scrollPosition - scrollAmount): Math.min(container.scrollWidth - container.clientWidth, scrollPosition + scrollAmount);container.scrollTo({left: newPosition,behavior: 'smooth'});setScrollPosition(newPosition);};return (<section id="blogs" className="blogs-section bg-white relative z-1"><div className="blogs-container"><Title level={1} className="section-title">博客</Title>{/* 分类筛选 */}<div className="category-filter-container" style={{ position: 'relative', width: '100%' }}><button className="scroll-button left" onClick={() => handleScroll('left')}style={{display: scrollPosition > 0 ? 'block' : 'none',position: 'absolute',left: 0,top: '50%',transform: 'translateY(-50%)',zIndex: 1,background: '#fff',border: '1px solid #ddd',borderRadius: '50%',width: '32px',height: '32px',cursor: 'pointer',boxShadow: '0 2px 4px rgba(0,0,0,0.1)'}}>←</button><div ref={categoryFilterRef}className="category-filter" style={{ display: 'flex', gap: '8px', overflowX: 'auto',scrollBehavior: 'smooth',padding: '0 40px',msOverflowStyle: 'none',scrollbarWidth: 'none','&::-webkit-scrollbar': { display: 'none' },'@media (max-width: 768px)': {padding: '0 32px',gap: '6px'}}}onScroll={(e) => setScrollPosition(e.currentTarget.scrollLeft)}>{categories.map(category => (<buttonkey={category}className={`category-button ${selectedCategory === category ? 'active' : ''}`}onClick={() => {setSelectedCategory(category);setCurrentPage(1);setBlogs([]);if (blogsGridRef.current) {blogsGridRef.current.scrollTop = 0;}}}style={{ flexShrink: 0,padding: '8px 16px',borderRadius: '20px',border: '1px solid #e8e8e8',background: selectedCategory === category ? '#1890ff' : '#fff',color: selectedCategory === category ? '#fff' : '#333',transition: 'all 0.3s ease',fontSize: window.innerWidth <= 768 ? '14px' : '16px',whiteSpace: 'nowrap'}}>{category}</button>))}</div><button className="scroll-button right" onClick={() => handleScroll('right')}style={{display: categoryFilterRef.current && scrollPosition < (categoryFilterRef.current.scrollWidth - categoryFilterRef.current.clientWidth) ? 'block' : 'none',position: 'absolute',right: 0,top: '50%',transform: 'translateY(-50%)',zIndex: 1,background: '#fff',border: '1px solid #ddd',borderRadius: '50%',width: '32px',height: '32px',cursor: 'pointer',boxShadow: '0 2px 4px rgba(0,0,0,0.1)'}}>→</button></div>{/* 博客列表(限制高度并滚动) */}<div className="blogs-grid" ref={blogsGridRef}>{blogs.map((blog) => (<div key={blog.id} className="blog-item" onClick={() => window.open(blog.url, '_blank')} style={{ cursor: 'pointer' }}><Imagesrc={blog.image}alt={blog.title}className="blog-image"preview={false}/><div className="blog-info"><h3 className="blog-title">{blog.title}</h3><div className="blog-meta"><span>{blog.abstract}</span></div></div></div>))}{loading && (<div className="loading-container"><Spin size="large" /></div>)}</div></div></section>);
};export default Blogs;
分析与解决
以下是针对“快速切换标签页导致数据展示错乱”问题的解决方案及技术解析,适用于前端开发场景(以Vue框架为例):
一、问题现象与原因分析
场景复现:
当用户快速点击不同标签页时,前一个标签页的异步请求尚未完成,后一个标签页的请求已发出。若前一个请求响应较慢,返回时覆盖了当前标签页的数据,导致显示错误。
核心原因:
- 异步请求竞态:未处理未完成的请求,新旧请求响应顺序不可控
- 组件状态未隔离:标签页组件复用导致数据残留
- 缓存策略冲突:浏览器或框架缓存旧数据未更新
二、解决方案与代码实现
方案1:请求中断(竞态控制)
通过AbortController
取消未完成的请求,确保只处理最新请求结果。
// Vue3 Composition API 示例
import { ref } from 'vue'
import axios from 'axios'export default {setup() {const activeTab = ref('tab1')let controller = nullconst fetchData = async (tabId) => {// 取消前一个请求if (controller) controller.abort()controller = new AbortController()try {const response = await axios.get(`/api/${tabId}`, {signal: controller.signal})// 更新对应标签页数据data.value = response.data} catch (err) {if (err.name !== 'CanceledError') {console.error('请求失败:', err)}}}watch(activeTab, (newVal) => {fetchData(newVal)})return { activeTab, data }}
}
方案2:状态标记(请求时序校验)
通过唯一标识验证是否为最新请求,避免旧数据覆盖。
let requestId = 0const fetchData = async (tabId) => {const currentId = ++requestIdconst response = await axios.get(`/api/${tabId}`)// 仅处理最新请求if (currentId === requestId) {updateData(tabId, response.data)}
}
方案3:组件隔离(Vue Keep-Alive优化)
结合<keep-alive>
与动态组件,实现标签页缓存与状态隔离。
<template><keep-alive :include="cachedTabs"><component :is="activeComponent" :key="activeTab" @data-loaded="handleData"/></keep-alive>
</template><script>
export default {data() {return {cachedTabs: [],activeTab: 'TabA'}},computed: {activeComponent() {return defineAsyncComponent(() => import(`@/components/${this.activeTab}`))}},methods: {handleData(newData) {// 根据activeTab更新对应数据}}
}
</script>
方案4:防抖节流(UI层控制)
限制高频切换的请求频率,减少无效请求。
import { debounce } from 'lodash'export default {methods: {handleTabClick: debounce(function(tabId) {this.fetchData(tabId)}, 300)}
}
三、最佳实践建议
1. 混合策略:优先使用请求中断 + 状态标记,配合组件隔离
2. 缓存管理:
• 设置Cache-Control: no-cache
请求头
• 对需要缓存的标签页使用<keep-alive>
的include
白名单
3. 错误处理:
axios.interceptors.response.use(response => {if (response.config.signal?.aborted) {return Promise.reject(new Error('Request aborted'))}return response
})
4. 性能监控:
• 使用Navigation Timing API
统计请求耗时
• 对超过1s的请求添加加载状态提示
四、延伸思考
- 数据一致性:当多个标签页共享数据时,可使用
Vuex/Pinia
统一状态管理 - SSR场景:服务端渲染时通过
asyncData
预请求避免客户端竞态 - Web Worker:将数据处理移入Worker线程,避免主线程阻塞
五、总结
通过请求中断、状态验证、组件隔离等综合手段,可有效解决快速切换导致的错乱问题。建议根据实际场景选择组合策略,并通过性能监控持续优化用户体验。
再回味
什么是异步请求的竞态条件
竞态条件(race condition)是指两个或多个异步操作同时访问或修改同一资源,由于时序不可预测,最终结果依赖于操作完成的先后顺序,引发不可预期或错误的行为。(Arcjet blog, GuidePoint Security) 在 JavaScript 单线程模型中,事件循环的微任务与宏任务调度也可能导致类似的时机问题。(MDN Web Docs)
引发竞态条件的原因
- 浮动承诺(Floating Promises):未正确链式返回的 Promise 可能在后续处理程序 attach 之前就已完成,导致逻辑错乱。(MDN Web Docs)
- 并发无序执行:多次发起相同请求,且无前置校验或取消机制,可能导致响应回包次序与预期不符。(Medium)
- 缺乏锁与队列机制:在并行执行环境中,若对共享数据缺少互斥访问控制,也会出现时序冲突。(Stack Overflow)
应用场景
前端并发数据请求
用户界面中,当组件同时发起多个 API 请求,且部分请求依赖前者结果时,若无严格顺序控制,将导致 UI 状态错误。(MDN Web Docs) 在 React 中,类似场景下还会触发内存泄漏或更新已卸载组件的警告。(Sébastien Lorber)
多用户提交场景
在高并发表单提交或投票系统中,多用户同时操作同一资源,若接口缺乏幂等性保护或事务隔离,将产生重复写入或丢失更新。(Welcome) 必要时可通过压力测试验证系统是否存在不一致状态。(Software Engineering Stack Exchange)
后端微服务调用
微服务架构下,多个服务并行调用同一下游接口或数据库,若缺少分布式锁,可能因调用顺序错乱导致数据写入冲突或半成品状态。(Node.js Design Patterns) 合理设计分布式锁或幂等端点,可显著降低此类风险。(GeeksforGeeks)
竞态条件问题示例
// 示例:累加文件大小的竞态条件
async function totalSize(folder) {const files = await folder.getFiles();let total = 0;// Promise.all 并行执行,累加操作可能乱序await Promise.all(files.map(async file => {total += await file.getSize();}));return total; // 结果往往比预期小
}
如上例中,并行更新外部变量 total
,因多线程并发计算并累加,最终结果可能不正确。(Hacker News)
// 示例:多个 AJAX 请求不合并
function fetchData() {// 连续发起两次请求ajax('/api/data', res1 => { console.log(res1); });ajax('/api/data', res2 => { console.log(res2); });
}
在此场景下,若不合并请求或去重,响应次序与数据一致性无法保证。(Stack Overflow)
解决方案
1. 避免多次重复请求
- 请求去重:维护一个进行中请求的映射表,若发现已存在相同 URL 或相同参数的请求,则复用前者 Promise;否则发起新请求。(Reddit)
- 节流/防抖:对频繁触发的操作(如搜索联想、按钮多次点击)进行节流或防抖处理,降低请求量。(MDN Web Docs)
2. 使用锁(Mutex)或队列
- 异步互斥锁:引入 async-mutex 等库,确保同一时刻只有一个任务持有锁,其他任务排队等待。(Stack Overflow)
- 队列机制:将需要依次执行的任务放入队列,按顺序依次出队并执行,保证顺序不被打乱。(DEV Community)
3. 使用幂等性设计
- 幂等接口:对提交操作设计幂等性(如使用幂等 ID、幂等 token),服务端检测重复请求后返回相同结果,而不会重复执行写操作。(DEV Community)
- 版本号/ETag:在资源上使用版本号或 ETag,客户端提交时带上版本信息,服务端判断版本一致后才允许更新,否则返回冲突。(GeeksforGeeks)
4. 利用 Promise.race 及超时取消
- Promise.race:在多个同类请求中,只关心最先完成的那个或最先返回成功的结果。(MDN Web Docs)
- 超时控制:结合
AbortController
,给请求设置超时或在组件卸载时取消未完成请求,避免过时结果覆盖状态。(GreatFrontEnd)
5. 序列号/时间戳校验
- 附加序列号:每次请求附带单调递增的序列号或时间戳,响应回包后,只有序列号最新的结果才能更新状态,忽略过时响应。(GreatFrontEnd)
- 前后端校验:后端同样校验请求携带的时间戳或版本,拒绝处理过时请求。(MDN Web Docs)
6. 乐观/悲观并发控制
- 乐观锁(Optimistic Locking):假定冲突较少,通过版本号或校验码检测冲突后重试。(GuidePoint Security)
- 悲观锁(Pessimistic Locking):在操作开始前先锁定资源,直到操作完成后再释放锁,适用于高冲突场景。(Arcjet blog)
7. 后端分布式锁方案
- 基于 Redis 的分布式锁:利用
SETNX
、Lua 脚本或 RedLock 算法实现多实例环境下的互斥访问。(GeeksforGeeks) - 基于 Zookeeper/Etcd:使用强一致性的协调服务构建可靠分布式锁,适用于关键业务路径。(Node.js Design Patterns)
8. 结合 AbortController 中止请求
- 在前端,使用
new AbortController()
创建控制器,将signal
传递给fetch()
;在组件卸载或新请求发起时调用controller.abort()
,可优雅取消未完成请求,避免旧响应影响新状态。(wanago.io) - 后端 Node.js 环境中,也可采用相同 API 取消长时异步任务,如数据库查询或文件 I/O。(betterstack.com)
总结
异步请求竞态条件在现代 Web 开发中无处不在。通过本文所述的多种技术手段——从前端请求去重与互斥锁,到幂等性设计、序列号校验,再到后端分布式锁——可有效防范并发冲突,确保系统状态一致与稳定。针对不同场景灵活选型与组合以上方案,是提升应用健壮性与用户体验的关键。
相关文章:
[前端]异步请求的竞态问题
竞态条件简介 遇到的问题 切换标签请求数据,但又快速切换标签请求数据,展示的是前一个标签的数据, 需要在切换标签时添加取消请求的机制,使用AbortController来取消正在进行的请求。当用户快速切换标签时,取消之前的请…...
【PDF拆分+提取内容改名】批量拆分PDF提取拆分后的每个PDF物流面单数据改名或导出表格,基于WPF的PDF物流面单批量处理方案
应用场景 物流行业每天需要处理大量包含物流面单的PDF文件,这些文件通常包含运单号、收发货人信息、货物详情等重要数据。传统手动处理方式效率低下且容易出错。本方案通过WPF实现一个自动化工具,能够: 批量拆分多页PDF为单页文件提取每页面单中的关键信息(如运单号、收件人…...
adb无线调试步骤
环境: macOS; 换成 linux 或 windows 也支持的小米15 Pro; 换成其他 android 手机也支持的 电脑和手机接入相同Wifi在电脑上,确保安装了 adb 对于 Android 开发者, 一般是是通过 Android Studio 安装对于 ndk 开发者…...
RocketMQ与Kafka的区别
文章目录 相同之处不同之处存储形式性能对比传输系统调用存储可靠性单机支持的队列数延时消息消息重复消息过滤消息失败重试死信队列 DLQ回溯消息分布式事务服务发现开发语言友好性开源社区活跃度商业支持成熟度 总结Kafka 和 RocketMQ 怎么选? 本文参考:…...
剥开 MP4 的 千层 “数字洋葱”:从外到内拆解通用媒体容器的核心
在当今数字化时代,MP4 格式随处可见,无论是在线视频、手机拍摄的短片,还是从各种渠道获取的音频视频文件,MP4 都占据着主流地位。它就像一个万能的 “数字媒体集装箱”,高效地整合和传输着各种视听内容。接下来&#x…...
设计模式(结构型)-组合模式
定义 组合模式的定义为:将对象组合成树形结构以表示 “部分 - 整体” 的层次结构,并且使得用户对单个对象和组合对象的使用具有一致性。其最关键的实现要点在于,简单对象和复合对象必须实现相同的接口,这一特性正是组合模式能够对…...
使用 IDEA + Maven 搭建传统 Spring MVC + Thymeleaf 项目的详细步骤
使用 IDEA Maven 搭建传统 Spring MVC Thymeleaf 项目 环境准备步骤 1:创建 Maven 项目步骤 2:添加依赖(pom.xml)步骤 3:配置 web.xml步骤 4:Spring 配置类(Java Config)步骤 5&am…...
「Mac畅玩AIGC与多模态19」开发篇15 - 判断节点与工具节点联动示例
一、概述 本篇在引入工具节点的基础上,进一步结合判断节点(条件分支),实现根据用户输入内容动态控制是否调用外部接口。通过构建“用户是否需要天气信息”的条件逻辑,开发人员将掌握如何在 Dify 工作流中通过条件判断…...
docker 外部能访问外网,内部不行(代理问题)
如果宿主机访问外网依赖代理(比如 http_proxy 环境变量),容器默认不会继承。需要显式传入代理: docker run -e http_proxy... -e https_proxy... ...在 docker-compose 中配置 HTTP/HTTPS 代理 version: 3 services:app:image: …...
模糊控制理论(含仿真)
本文讲解模糊控制理论、设计步骤以及案例。 1. 模糊控制原理: 模糊控制(Fuzzy Control)是一种基于模糊逻辑推理的人类经验规则实现的控制方法,适用于对系统模型不精确或难以建立精确数学模型的复杂系统。它利用“如果…那么…”&…...
《 C++ 点滴漫谈: 三十六 》lambda表达式
一、引言 在 C98 和 C03 时代,尽管 C 拥有强大的泛型编程能力和丰富的面向对象特性,但在表达局部逻辑、回调行为或一次性函数处理时,程序员却常常需要冗长的代码来定义函数对象(functor),或者使用函数指针…...
【C/C++】函数模板
🎯 C 学习笔记:函数模板(Function Template) 本文是面向 C 初学者的函数模板学习笔记,内容包括基本概念、定义与使用、实例化过程、注意事项等,附带示例代码,便于理解与复现。 📌 一…...
电赛经验分享——模块篇
1、前言 打算在这一个专栏中,分享一些本科控制题电赛期间的经验,和大家共同探讨,也希望能帮助刚刚参加电赛的同学,了解一些基本的知识。一些见解和看法可能不同或有错误,欢迎批评指正。 在本文中,主要介绍笔…...
LeetCode 热题 100 70. 爬楼梯
LeetCode 热题 100 | 70. 爬楼梯 大家好,今天我们来解决一道经典的动态规划入门题——爬楼梯。这道题在LeetCode上被标记为简单难度,要求我们计算爬到第n阶楼梯的不同方法数,每次可以爬1或2个台阶。下面我将详细讲解解题思路,并附…...
浔川AI测试版内测报告
浔川AI测试版内测报告 一、引言 本次对浔川AI测试版进行内测,旨在全面评估其功能表现与性能状况,为后续的优化升级及正式上线提供有力依据。 二、测试环境 1. 硬件环境:[Windows 10】 2. 软件环境:操作系统为【核桃编程]ÿ…...
Leetcode刷题记录31——旋转图像
题源:https://leetcode.cn/problems/rotate-image/description/?envTypestudy-plan-v2&envIdtop-100-liked 题目描述: 思路一: 💡 解题思路:分两步完成旋转 虽然“直接旋转”看起来有点抽象,但我们…...
攻防世界-php伪协议和文件包含
fileinclude 可以看到正常回显里面显示lan参数有cookie值表示为language 然后进行一个判断,如果参数不是等于英语,就加上.php,那我们就可以在前面进行注入一个参数,即flag, payload:COOKIE:languageflag …...
[C++] 小游戏 决战苍穹
大家好,各位看到这个标题,斗破苍穹什么时候改叫决战苍穹了?其实,因为版权等一系列问题,斗破苍穹正式改名为决战苍穹,这个版本主要更新内容为解决了皇冠竞技场太过影响游戏平衡,并且提高了一些装…...
项目成本管理_挣得进度ES
在项目成本管理的新实践中, 通过挣值管理(EVM) 的扩展,引入 挣得进度ES 这一概念, ES是EVM理论和实践的延伸,挣得进度理论用ES和实际时间(AT) 替代了传统EVM所使用的进度偏差测量指标SV(挣值—计划价值)。 使用这种替代方法计算…...
矩阵快速幂 快速求解递推公式
文章目录 习题790.多米诺和托米诺平铺 对于一个给定的递推公式,例如dp[i] dp[i-1] * a dp[i-2] * b,那么常用的做法,肯定是使用o(n)的时间复杂度进行线性求解,但是如果 n 10 18 n{10}^{18} n1018,那么肯定超时的,这…...
驱动开发硬核特训 · Day 28(上篇):pinctrl 子系统详解与实战分析
📅 日期:2025-05-05 📚 技术平台:嵌入式Jerry(B站) 一、引言 在嵌入式系统中,SoC 芯片的引脚通常具有多种功能,如 GPIO、UART、I2C、SPI 等。为了在不同的应用场景中灵活配置引脚功…...
20250505下载VLC for Android
20250505下载VLC for Android 2025/5/5 14:35 缘起:做Rockchip的RK3566的Android13下的跨网段PING。 酷芯的图传网段 和 softAP/以太网RJ45共享网段之间互相PING通。 图传的原厂/供应商说可以使用ffmpeg进行rtsp流的转发。 后来确认VLC for Android版本只有接受流&a…...
Jetpack Compose 响应式布局实战:BoxWithConstraints 完全指南
深入理解 Jetpack Compose 中的 BoxWithConstraints 前言 在构建现代 Android 应用时,响应式设计已成为必不可少的要求。Jetpack Compose 作为 Android 的现代 UI 工具包,提供了 BoxWithConstraints 这一强大组件,帮助我们轻松创建能够适应…...
ZYNQ笔记(十七):IP核封装与接口定义
版本:Vivado2020.2(Vitis) 任务:将“HDMI彩条显示实验”(正点原子 ZYNQ FPGA 开发视频)中所实现的 RGB2DVI 模块封装成一个 IP 核。 目录 一、介绍 (1)IP核 (2&#x…...
学习笔记msp430f5529lp
注:本文仅用于个人学习使用,记录笔记。 学习视频msp430f5529库函数入门教程 00.序言_哔哩哔哩_bilibili 向大佬致敬理工男小帅-CSDN博客 CCS环境快捷键使用 代码注释:Ctrl/ 提示/补全: CtrlShiftC 放大:Ctrl 缩小:Ctrl- 切换选择模式&…...
人工智能应用:从技术突破到生态重构的演进之路
一、人工智能的发展历程:从符号主义到通用智能探索 人工智能(AI)的发展始于20世纪中叶,其历程可划分为四个关键阶段: 符号主义与早期探索(1950s-1970s) 以逻辑推理和专家系统为核心&…...
【ZYNQ Linux移植】4-内核移植
文章目录 0 写在前面1 内核源码的文件结构2 Linux内核移植2.1 移植配置文件2.2 移植设备树2.3 创建脚本进行编译2.4 备份相关文件 3 测试4 总结5 参考资料 0 写在前面 这是一个系列博客,详细介绍如何在 ZYNQ 与 ZYNQ MP 平台上如何移植 Linux 系统。目前网络上的大部…...
代码随想录算法训练营第三十二天
LeetCode/卡码网题目: 518. 零钱兑换 II377. 组合总和 Ⅳ790. 多米诺和托米诺平铺(每日一题)57. 爬楼梯(第八期模拟笔试) 其他: 今日总结 往期打卡 背包问题特点: 滚动数组背包遍历顺序 完全背包从小到大,即基于当前物品更新过的继续更新01背包从大到…...
java CompletableFuture 异步编程工具用法1
1、测试异步调用: static void testCompletableFuture1() throws ExecutionException, InterruptedException {// 1、无返回值的异步任务。异步线程执行RunnableCompletableFuture.runAsync(() -> System.out.println("only you"));// 2、有返回值的异…...
Spring Boot 集成 Solr 的详细步骤及示例
环境准备 安装 Solr :从 Solr 官网(Welcome to Apache Solr - Apache Solr)下载并安装最新版本,然后通过命令 bin/solr start 启动 Solr 服务,使用 bin/solr create -c mycore 创建一个新的 Solr 核心。 安装 JDK &am…...
Nemotron-Research-Tool-N1 如何提升大语言模型工具使用能力?
Nemotron-Research-Tool-N1如何提升大语言模型工具使用能力? 如今,大语言模型(LLMs)发展迅猛,给它配备外部工具成为研究热点。但传统方法存在不少问题。这篇论文提出的Nemotron-Research-Tool-N1系列模型带来新突破&a…...
OpenCV进阶操作:图像直方图、直方图均衡化
文章目录 一、图像直方图二、图像直方图的作用三、使用matplotlib方法绘制直方图2.使用opencv的方法绘制直方图(划分16个小的子亮度区间)3、绘制彩色图像的直方图 四、直方图均衡化1、绘制原图的直方图2、绘制经过直方图均衡化后的图片的直方图3、自适应…...
Android控件VideoView用法
一 控件UI <VideoViewandroid:id="@+id/videoView"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="fitCenter" /> 二 配置 <?xml version="1.0" encoding="u…...
人工智能数学基础(十)—— 图论
图论作为数学的重要分支,为人工智能提供了强大的建模和分析工具。无论是社交网络分析、路径规划还是数据结构设计,图论都发挥着不可替代的作用。今天,我将带领大家深入浅出地探索图论的核心概念,并结合 Python 实例,让…...
深入探索Anthropic Claude与Spring AI的融合应用
深入探索Anthropic Claude与Spring AI的融合应用 前言 在人工智能的蓬勃发展进程中,自然语言处理领域不断涌现出强大的模型和工具。Anthropic Claude系列基础AI模型凭借其出色的性能,在各种应用场景中展现出巨大潜力,为开发者和企业提供了丰…...
Python爬虫实战:获取优美图库各类高清图片,为用户提供设计素材
一、引言 在互联网时代,高清壁纸资源丰富多样,而优美图库作为一个提供大量精美壁纸的网站,吸引了众多用户。通过 Python 爬虫技术,可以自动化地从该网站获取所需的壁纸资源,为用户节省时间和精力。然而,网站通常会采取反爬措施来防止数据被恶意抓取,因此需要在爬虫程序…...
Java常用注解大全(基于JDK17+SpringBoot3)
一、基础注解(Java原生) 编译相关 @Override:方法重写校验 java 复制 下载 @Override public String toString() { return "CustomObj"; } @Deprecated:标记过时元素 java 复制 下载 @Deprecated(since="1.8", forRemoval=true) public void oldMethod…...
【NLP】30. 深入理解 In-Context Learning 的核心机制与策略
In-Context Learning(ICL)详解:提示学习时代的语言理解 一、什么是 In-Context Learning(ICL)? In-Context Learning 是指: 不改变模型参数,通过在输入中加入示例(demon…...
数字化工厂中央控制室驾驶舱系统 - Windows 部署笔记
数字化工厂中央控制室驾驶舱系统 - Windows 部署笔记 环境准备 这篇笔记记录了我在 Windows 10/11 上部署数字化工厂中央控制室驾驶舱系统的全过程,包括各种常见问题的解决方法。部署过程中使用了国内镜像源来加快下载速度。 前置需求 Python:3.8 到…...
数据库的原子事务
原子事务 11.1 全有或全无效应 二级索引需要原子性的多键更新,这不仅对数据库内部一致性至关重要,也对应用数据的一致性非常有用(例如考虑账户余额和账户交易)。 我们将放弃get-set-del接口,并添加一个新的接口来允…...
基于51单片机的红外人体感应报警器
基于51单片机的人体监测报警 (仿真+程序+原理图+PCB) 功能介绍 具体功能: 1.按下报警按钮会发生红LED蜂鸣器声光报警; 2.若检测到人,黄LED打开; 3.按下布防按键&…...
从Excel到高级工具:数据分析进阶指南
从Excel到高级工具:数据分析进阶指南 在数据分析的世界里,Excel曾经是众多人的第一站。它简单、直观、功能强大,从普通用户到专业人士,无不对其依赖。然而,随着数据规模增长、分析需求升级,Excel渐渐显得力…...
Excel VBA 自定义函数
一、VBA 函数基础概念 在 Excel VBA 中,函数主要分为两种类型: Sub 过程:执行操作但不返回值Function 函数:执行操作并返回结果 基本语法示例 1. Function 函数示例 定义一个返回字符串的公共函数 Public Function GetGreetin…...
004-nlohmann/json 快速认识-C++开源库108杰
了解 nlohmann/json 的特点;理解编程中 “数据战场”划分的概念;迅速上手多种方式构建一个JSON对象; 1 特点与安装 nlohmann/json 是一个在 github 长期霸占 “JSON” 热搜版第1的CJSON处理库。它的最大优点是与 C 标准库的容器数据…...
【Quest开发】接入语音转文字
参考官方文档:https://developers.meta.com/horizon/documentation/unity/voice-sdk-tutorials-overview 软件:Unity 2022.3.51f1c1、vscode、Meta XR All in One SDK V72 硬件:Meta Quest3 注意:需全程科学上网 Meta提供了一…...
Vim 命令从头学习记录
学习链接:eleon-vim基础教程 Vim - 基础翻屏操作 光标移动:hjkl 20j 向下移动20行,w 向后移动一个字符,b 向前移动一个字符。 Ctrl u 向上翻半页 UP Ctrl d 向下翻半页 Down Ctrl f 向下翻整页 Forward Ctrl b 向上翻整页 …...
[Linux]物理地址到虚拟地址的转化
[Linux]物理地址到虚拟地址的转化 水墨不写bug 文章目录 一、再次认识地址空间二、页表1、页表的结构设计2、页表节省了空间,省在哪里?3、页表的物理实现 一、再次认识地址空间 OS和磁盘交互的内存基本单位是4KB,这4KB通常被称为内存块。OS对…...
js获取明天日期、Vue3大菠萝 Pinia的使用
直接上代码 const today new Date(2019, 2, 28) const finalDate new Date(today) finalDate.setDate(today.getDate() 3)console.log(finalDate) // 31 March 2019 安装 yarn add pinia # or with npm npm install pinia创建第一个store仓库 1、在src目录下创建store目录…...
矩阵置零(中等)
可以用两个标记数组分别记录每一行和每一列是否有零出现。 首先遍历该数组一次,如果某个元素为 0,那么就将该元素所在的行和列所对应标记数组的位置置为 true。然后再次遍历该数组,用标记数组更新原数组。 class Solution {public void set…...
GZ人博会自然资源系统(测绘)备考笔记
本文为备考 GZ人才博览会自然资源系统(测绘) 的笔记,包括若干 知识点整理 及 近两年考核(面试)真题 (文末附《GZ人博会自然资源系统(测绘)备考笔记》1 的下载链接)。 目录…...