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

线程安全 ≠ 协程安全:当全局缓存同时遇上线程池和 async,优秀 Python 工程师该如何设计?

线程安全 ≠ 协程安全当全局缓存同时遇上线程池和 async优秀 Python 工程师该如何设计Python 让很多人第一次感受到编程的温柔语法简洁生态丰富既能写 Web 服务也能做数据分析、自动化脚本、机器学习和 AI 应用。但越是“简单好用”的语言越容易在工程深水区暴露细节。比如今天这个问题一个全局缓存既会被线程池中的同步任务访问又会被asyncio异步上下文访问。线程安全和协程安全有什么区别threading.Lock和asyncio.Lock能混用吗这是一个非常典型的 Python 实战问题。它不只是锁的选择问题而是并发模型、资源边界和工程责任的问题。一、先给结论不能简单混用threading.Lock和asyncio.Lock不能当成同一种锁混用。更准确地说threading.Lock 保护的是线程之间的共享资源。 asyncio.Lock 保护的是同一个事件循环内协程之间的共享资源。它们面对的调度模型不同线程由操作系统抢占式调度可能在任意时刻被切换。 协程由事件循环协作式调度通常在 await 处让出执行权。因此在线程池和异步上下文同时访问全局缓存时最危险的做法是# 错误倾向以为用了 asyncio.Lock 就能保护线程池里的访问cache{}async_lockasyncio.Lock()或者# 错误倾向在 async 函数中直接用 threading.Lock 包住 awaitthread_lockthreading.Lock()asyncdefbad():withthread_lock:awaitsome_io()前者保护不了线程池里的同步代码后者可能阻塞事件循环甚至造成死锁和吞吐下降。二、线程安全面对“抢占式切换”的安全线程安全关注的是多个线程同时访问同一份可变数据时程序是否仍然正确。例如一个全局缓存cache{}defget_user(user_id):ifuser_idnotincache:cache[user_id]load_user_from_db(user_id)returncache[user_id]这段代码看似简单但在多线程下有竞态条件。两个线程可能同时发现user_id not in cache然后同时去数据库加载再同时写入缓存。轻则重复请求重则写入脏数据。正确做法之一是使用threading.Lockimportthreading cache{}cache_lockthreading.Lock()defget_user(user_id):withcache_lock:ifuser_idnotincache:cache[user_id]load_user_from_db(user_id)returncache[user_id]但这里还有一个性能问题如果load_user_from_db很慢那么锁会被长时间持有其他线程都被挡住。更好的做法是区分“检查缓存”和“加载数据”的边界importthreading cache{}cache_lockthreading.Lock()defget_user(user_id):withcache_lock:usercache.get(user_id)ifuserisnotNone:returnuser userload_user_from_db(user_id)withcache_lock:existingcache.get(user_id)ifexistingisnotNone:returnexisting cache[user_id]userreturnuser这不是完美的单飞请求合并但比长时间持锁更健康。它体现了一个重要原则锁应该尽量保护共享状态而不是保护整个业务流程。三、协程安全面对“await 让出执行权”的安全协程安全关注的是多个协程在同一个事件循环中交替运行时共享状态是否仍然正确。很多初学者以为async 是单线程所以不会有并发问题。这也是误区。看这个例子counter0asyncdefincrease():globalcounter valuecounterawaitasyncio.sleep(0)countervalue1如果同时运行多个协程importasyncio counter0asyncdefincrease():globalcounter valuecounterawaitasyncio.sleep(0)countervalue1asyncdefmain():awaitasyncio.gather(*(increase()for_inrange(1000)))print(counter)asyncio.run(main())你可能期待输出1000但结果可能远小于 1000。原因是协程虽然不是被操作系统随时打断但它会在await处主动让出控制权。多个协程可能读取到同一个旧值然后覆盖彼此的更新。此时应该使用asyncio.Lockimportasyncio counter0lockasyncio.Lock()asyncdefincrease():globalcounterasyncwithlock:valuecounterawaitasyncio.sleep(0)countervalue1不过这里也有一个实践提醒不要在锁内做太多耗时 I/O。虽然asyncio.Lock不会阻塞整个线程但它会让其他等待同一把锁的协程排队。四、线程安全和协程安全的核心差异可以用一张表概括。对比项线程安全协程安全调度方式操作系统抢占式调度事件循环协作式调度切换时机理论上可能发生在很多地方通常发生在await处常用锁threading.Lock、RLockasyncio.Lock是否阻塞线程会阻塞当前线程不阻塞事件循环线程但会挂起协程保护范围多线程共享数据同一事件循环内的协程共享数据是否跨线程有效是否不应用于线程池同步代码是否可在 async 中随便用不建议是 async 场景首选一句话threading.Lock 是给线程看的。 asyncio.Lock 是给协程看的。它们不是替代关系而是服务于不同并发世界的工具。五、错误示例在 async 中直接使用 threading.Lock假设你写了这样的代码importthreadingimportasyncio cache{}lockthreading.Lock()asyncdefget_value(key):withlock:ifkeyincache:returncache[key]valueawaitfetch_from_remote(key)cache[key]valuereturnvalue这段代码有两个大问题。第一threading.Lock的获取是阻塞式的。如果另一个线程持有这把锁当前事件循环线程会被卡住整个 async 服务都可能变慢。第二更严重的是你在持有threading.Lock的时候执行了await。这意味着当前协程让出了执行权但锁还没释放。其他线程或代码如果也在等待这把锁就可能形成非常难排查的阻塞链。正确原则是不要在 threading.Lock 保护区内 await。 不要用阻塞锁包裹异步 I/O。六、错误示例用 asyncio.Lock 保护线程池共享数据再看另一个错误方向importasynciofromconcurrent.futuresimportThreadPoolExecutor cache{}lockasyncio.Lock()defworker(key):# 线程池里的同步函数无法 async with lockifkeynotincache:cache[key]compute(key)returncache[key]asyncio.Lock必须通过async with和await使用它属于事件循环机制。线程池里的同步函数无法直接等待它。即使你想强行绕也会让代码变得脆弱、复杂而且容易出现事件循环绑定、跨线程调度和死锁问题。正确原则是asyncio.Lock 不能保护线程池里的普通同步函数。 线程池访问共享数据时需要线程锁或更清晰的资源所有权模型。七、真实场景全局缓存同时被线程池和 async 访问假设我们有一个图片处理服务。API 层是异步的app.get(/image/{image_id})asyncdefget_image(image_id:str):...但图像处理是 CPU 密集型会丢到线程池或进程池中resultawaitloop.run_in_executor(pool,process_image,image_id)同时API 层和 worker 都要访问一个全局缓存cache{image:001:b...}这时如果随便在 async 代码里用asyncio.Lock线程池不受保护如果全局都用threading.Lockasync API 又可能被阻塞。怎么办推荐三种方案。八、方案一统一用线程安全缓存async 层通过to_thread访问如果缓存必须被线程池和 async 层共同访问可以把缓存设计成一个纯同步、线程安全的组件。importthreadingimporttimefromtypingimportAnyclassThreadSafeTTLCache:def__init__(self,ttl_seconds:int300):self._data:dict[str,tuple[float,Any]]{}self._ttlttl_seconds self._lockthreading.RLock()defget(self,key:str)-Any|None:nowtime.time()withself._lock:itemself._data.get(key)ifitemisNone:returnNoneexpire_at,valueitemifexpire_atnow:delself._data[key]returnNonereturnvaluedefset(self,key:str,value:Any)-None:expire_attime.time()self._ttlwithself._lock:self._data[key](expire_at,value)defdelete(self,key:str)-None:withself._lock:self._data.pop(key,None)线程池中可以直接使用cacheThreadSafeTTLCache()defprocess_image(image_id:str):cachedcache.get(image_id)ifcachedisnotNone:returncached resultheavy_compute(image_id)cache.set(image_id,result)returnresult异步上下文中不要直接调用可能阻塞的锁竞争逻辑可以通过asyncio.to_thread转到线程中执行importasyncioasyncdefasync_get_cache(key:str):returnawaitasyncio.to_thread(cache.get,key)asyncdefasync_set_cache(key:str,value):awaitasyncio.to_thread(cache.set,key,value)API 层asyncdefget_image_result(image_id:str):cachedawaitasync_get_cache(image_id)ifcachedisnotNone:returncached loopasyncio.get_running_loop()resultawaitloop.run_in_executor(None,process_image,image_id)returnresult这个方案的好处是共享状态只有一种保护规则线程锁。缺点是async 访问缓存时有线程切换开销。但对于复杂服务来说这个开销通常比锁模型混乱更可控。九、方案二缓存只属于事件循环线程通过安全通道访问另一种思路是明确缓存归属权。比如规定缓存只允许在 async 事件循环中被直接访问。 线程池不能直接读写缓存。 线程池要访问缓存必须通过事件循环提交请求。可以使用asyncio.run_coroutine_threadsafeimportasynciofromtypingimportAnyclassAsyncCache:def__init__(self):self._data:dict[str,Any]{}self._lockasyncio.Lock()asyncdefget(self,key:str):asyncwithself._lock:returnself._data.get(key)asyncdefset(self,key:str,value:Any):asyncwithself._lock:self._data[key]value在线程中访问时defworker_get_cache(loop,cache:AsyncCache,key:str):futureasyncio.run_coroutine_threadsafe(cache.get(key),loop)returnfuture.result()这个方案更适合缓存强依赖 async 生命周期的场景。但它也有风险线程会等待事件循环返回结果。如果事件循环又在等待线程池结果就可能形成循环等待。因此必须谨慎设计调用方向。十、方案三把缓存独立成服务或 Actor在更复杂的系统里我更推荐把共享状态变成一个“独立所有者”。示意图Async API 层 | | 请求缓存 v Cache Manager / Actor ^ | 请求缓存 Thread Pool Worker缓存不再是一个到处可访问的全局 dict而是由一个专门组件管理。其他模块通过方法、队列、RPC 或消息传递访问它。这种思想叫不要共享内存来通信。 而是通过通信来共享状态。在 Python 项目中可以用本地队列 专门的缓存管理线程 Redis Memcached 数据库缓存表 消息队列如果是多进程部署进程内全局缓存本来就不可靠因为每个进程都有自己的内存副本。此时 Redis 这类外部缓存往往更合适。十一、最佳实践锁要短边界要清晰无论使用线程锁还是协程锁都要遵守几个原则。1. 不要在锁里做慢操作不推荐withlock:valueslow_network_call()cache[key]value推荐valueslow_network_call()withlock:cache[key]value对于 async 也是一样。不推荐asyncwithlock:valueawaitfetch_data()cache[key]value推荐valueawaitfetch_data()asyncwithlock:cache[key]value当然这可能带来重复加载问题。如果要避免重复加载需要引入更精细的 per-key lock 或 singleflight 模式。2. 锁保护的是不变量比如缓存的不变量可能是_data 中每个 key 对应的 value 必须没有过期。 _hits 和 _misses 的统计必须和访问行为一致。 某个 key 的构建过程不能重复执行。锁不是为了“看起来安全”而是为了保护这些不变量不被并发破坏。3. 避免全局大锁全局一把锁最简单但可能限制吞吐。可以使用 per-key lockimportthreadingfromcollectionsimportdefaultdict cache{}locksdefaultdict(threading.Lock)defget_or_compute(key):withlocks[key]:ifkeyincache:returncache[key]valuecompute(key)cache[key]valuereturnvalue这样不同 key 之间可以并发执行。但要注意defaultdict(threading.Lock)自身在极端并发下也可能需要保护生产环境可以用更严谨的锁管理器。4. async 中使用 per-key lock异步版本importasynciofromcollectionsimportdefaultdict cache{}locksdefaultdict(asyncio.Lock)asyncdefget_or_fetch(key):asyncwithlocks[key]:ifkeyincache:returncache[key]valueawaitfetch_value(key)cache[key]valuereturnvalue这适用于纯 async 场景。但如果线程池也要访问同一个cache这个方案仍然不够因为asyncio.Lock不保护线程。十二、threading.Lock与asyncio.Lock到底能不能混用答案要分层说。不能把它们当成同一把锁混用不可以这样理解我 async 代码用 asyncio.Lock。 线程代码用 threading.Lock。 它们锁的是同一个 cache所以应该安全。这是错误的。如果两个锁不是同一套互斥机制就可能出现协程拿到了 asyncio.Lock。 线程拿到了 threading.Lock。 二者同时修改同一个 cache。这等于没有真正互斥。可以在系统中同时存在但必须边界清晰一个服务里当然可以同时使用两种锁asyncio.Lock 保护 async 内部状态。 threading.Lock 保护线程共享状态。但同一份共享数据最好只归属于一种并发模型。如果一份数据必须跨线程和协程共享通常优先选择用 threading.Lock 作为底层保护 async 侧通过 to_thread 或专用适配层访问 或者彻底改为消息传递 / 外部缓存。十三、一个推荐模板混合场景下的缓存访问层下面是一个更接近生产代码的模板。importasyncioimportthreadingimporttimefromtypingimportCallable,TypeVar,Generic TTypeVar(T)classSafeCache(Generic[T]):def__init__(self,ttl_seconds:int300):self._ttlttl_seconds self._data:dict[str,tuple[float,T]]{}self._lockthreading.RLock()defget(self,key:str)-T|None:nowtime.time()withself._lock:itemself._data.get(key)ifitemisNone:returnNoneexpire_at,valueitemifexpire_atnow:self._data.pop(key,None)returnNonereturnvaluedefset(self,key:str,value:T)-None:expire_attime.time()self._ttlwithself._lock:self._data[key](expire_at,value)defget_or_compute(self,key:str,factory:Callable[[],T])-T:valueself.get(key)ifvalueisnotNone:returnvalue new_valuefactory()self.set(key,new_value)returnnew_valueclassAsyncCacheAdapter(Generic[T]):def__init__(self,cache:SafeCache[T]):self._cachecacheasyncdefget(self,key:str)-T|None:returnawaitasyncio.to_thread(self._cache.get,key)asyncdefset(self,key:str,value:T)-None:awaitasyncio.to_thread(self._cache.set,key,value)asyncdefget_or_compute(self,key:str,factory:Callable[[],T])-T:returnawaitasyncio.to_thread(self._cache.get_or_compute,key,factory)使用方式cacheSafeCache[bytes](ttl_seconds600)async_cacheAsyncCacheAdapter(cache)defsync_worker(image_id:str)-bytes:returncache.get_or_compute(image_id,lambda:process_image_sync(image_id))asyncdefasync_handler(image_id:str)-bytes:cachedawaitasync_cache.get(image_id)ifcachedisnotNone:returncached loopasyncio.get_running_loop()returnawaitloop.run_in_executor(None,sync_worker,image_id)这个设计的关键是真正的数据结构只由 SafeCache 管。 SafeCache 只使用 threading.RLock。 async 层不直接碰锁只通过适配器访问。这比“到处加锁”更清晰。十四、调试与测试不要相信感觉要制造竞争并发 bug 最可怕的地方是它们不一定每次复现。你可以写压力测试fromconcurrent.futuresimportThreadPoolExecutorimportasyncioimportrandom cacheSafeCache[int]()defsync_task(i:int):keyfk-{random.randint(1,10)}cache.set(key,i)returncache.get(key)asyncdefasync_task(i:int):keyfk-{random.randint(1,10)}awaitasyncio.to_thread(cache.set,key,i)returnawaitasyncio.to_thread(cache.get,key)asyncdefmain():loopasyncio.get_running_loop()withThreadPoolExecutor(max_workers8)aspool:thread_jobs[loop.run_in_executor(pool,sync_task,i)foriinrange(1000)]async_jobs[async_task(i)foriinrange(1000)]resultsawaitasyncio.gather(*thread_jobs,*async_jobs)print(len(results))asyncio.run(main())同时观察是否出现 KeyError 是否出现 RuntimeError 是否有死锁 是否事件循环卡顿 CPU 是否异常升高 P95 / P99 延迟是否恶化优秀工程师不会只说“我觉得这样安全”而是会用测试和监控证明它安全。十五、给初学者的记忆口诀可以记住这几句话线程锁管线程协程锁管协程。 asyncio.Lock 不保护线程池。 threading.Lock 可能阻塞事件循环。 不要在 threading.Lock 里 await。 同一份共享状态最好只有一个所有者。 不确定时先画出数据流再决定锁。这几句话比死背 API 更重要。十六、前沿视角未来 Python 并发会更重要Python 生态正在持续向高性能和多场景并发演进。FastAPI、asyncio、AnyIO、Trio、Pandas、NumPy、PyTorch、任务队列、GPU 计算、边缘设备和 AI 服务都让开发者越来越频繁地面对混合并发模型。但趋势越热越要回到基本功。真正的 Python 最佳实践不是“所有项目都 async”也不是“所有地方都加锁”而是理解瓶颈。 明确资源归属。 减少共享状态。 缩小锁范围。 用测试验证假设。在复杂系统中技术判断本身就是一种工程温度。你写下的每一把锁都是未来维护者要理解的一段承诺。十七、总结优秀工程师要能把“安全边界”讲清楚回到最初的问题线程安全与协程安全有什么差异threading.Lock与asyncio.Lock能混用吗最终答案是线程安全解决多线程抢占式并发下的共享数据问题。 协程安全解决同一事件循环中多个协程在 await 切换下的共享数据问题。 threading.Lock 和 asyncio.Lock 可以在同一系统中同时存在但不能随意混用来保护同一份共享状态。对于“全局缓存同时被线程池和异步上下文访问”这个场景我更推荐方案一用线程安全缓存作为底层唯一真相async 层通过 to_thread 访问。 方案二缓存归 async 事件循环所有线程通过安全通道提交请求。 方案三把缓存外置为 Redis / Memcached / 独立缓存服务。不要让一个全局 dict 成为系统里最脆弱、最隐蔽、最难排查的地方。Python 编程的高级感不在于你用了多少新语法而在于你能不能在复杂场景中写出清晰、可靠、可维护的代码。互动问题你在项目中是否遇到过“明明加了锁还是出现并发 bug”的情况你更倾向于在混合并发场景中使用本地缓存还是直接引入 Redis 这类外部缓存欢迎在评论区分享你的经验。很多时候工程能力就是在这些真实问题里一点点长出来的。

相关文章:

线程安全 ≠ 协程安全:当全局缓存同时遇上线程池和 async,优秀 Python 工程师该如何设计?

线程安全 ≠ 协程安全:当全局缓存同时遇上线程池和 async,优秀 Python 工程师该如何设计? Python 让很多人第一次感受到编程的温柔:语法简洁,生态丰富,既能写 Web 服务,也能做数据分析、自动化脚…...

SYS_NC00002$之类的列

参考文档: https://askmaclean.com/archives/oracle-virtual-column.html System Generatedcolumn Names inDBA IND COLUMNS KB836884 IMPDP Fails With ORA-14148 When Moving Tables Between 10g And 12c Instances When Optimization Is In Use KB181188 R…...

Cursor Free VIP破解工具:三步解决AI编程助手试用限制的终极方案

Cursor Free VIP破解工具:三步解决AI编程助手试用限制的终极方案 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: Youve reache…...

英文论文降AI率好难啊,改了一晚上AI率还增加了16%,到底怎么降AI率啊?

英文论文降AI率比中文AI率还要难降。 最可怕的是,现在很多同学自己写的英文论文,用翻译软件翻译成中文,结果去检测还是有AI率! 为什么? 因为现在的很多翻译软件也开始AI化了! 任何产品都在搞AI&#xf…...

终极屏幕翻译神器:Translumo让你的Windows电脑瞬间打破语言壁垒

终极屏幕翻译神器:Translumo让你的Windows电脑瞬间打破语言壁垒 【免费下载链接】Translumo Advanced real-time screen translator for games, hardcoded subtitles in videos, static text and etc. 项目地址: https://gitcode.com/gh_mirrors/tr/Translumo …...

量子增强神经辐射场(QNeRF)技术解析与应用

1. 量子增强神经辐射场(QNeRF)技术解析 量子计算与神经辐射场的结合正在重塑计算机视觉领域的3D重建范式。传统NeRF通过多层感知机(MLP)建立3D坐标到颜色和密度的映射,其核心公式可表示为: F_θ : (x, d) → (c, σ) 其中θ代表网络参数,x∈…...

【AI Infra 核心】端到端 AI Infra 工程师的炼丹炉:从内核系统到上层框架的调优全景图

🚀【AI Infra 核心】端到端 AI Infra 工程师的炼丹炉:从内核系统到上层框架的调优全景图摘要:我们的连载已经来到了第 9 篇。前面我们手撕了显存池、无锁队列、PagedAttention 和量化算法,一直在“盲人摸象”式地深挖各个底层技术…...

避开Unity PS5开发第一个大坑:手把手教你搞定Build-In管线环境与打包(基于2021.3.32f1)

避开Unity PS5开发第一个大坑:手把手教你搞定Build-In管线环境与打包(基于2021.3.32f1) 当你第一次尝试将Unity项目部署到PS5平台时,可能会遇到各种意想不到的障碍。从版本选择到SDK匹配,再到最终的打包流程&#xff0…...

676767899uijjk

bbbbbbbbghhjjjjuuyyjjjj...

堆 / 优先队列专题二刷笔记:前 K 个高频元素 数据流的中位数

目录 一、LeetCode 347. 前 K 个高频元素(中等) 题目描述 核心思路 方法 1:小顶堆(推荐,时间复杂度 O (n log k)) 方法 2:大顶堆(写法简单,但效率略低) …...

AI跑分飙升却无人问津,“说人话”才是模型出圈关键!

四月AI新动态四月,Anthropic发布Opus 4.7,OpenAI发布GPT 5.5,DeepSeek更新V4。三家公司发布通稿显示跑分、上下文、推理和代码能力提升,但互联网反应平淡,社交媒体讨论热度低,仅OpenAI的GPT - image出圈&am…...

小林大模型|大模型面试高频知识点合集2

什么是 Agent?与大模型有什么本质不同? 面试时答这道题,一定要点出三件事:一是 Agent 有自主规划能力,给它一个复杂目标它能自己拆解成多步;二是它能行动,通过工具调用跟外部世界真实交互&…...

急急急急急急急急哦吼吼吼叫

测试22333333...

免费解锁Windows虚拟显示器:Parsec VDD完整指南,游戏直播与远程办公的终极解决方案

免费解锁Windows虚拟显示器:Parsec VDD完整指南,游戏直播与远程办公的终极解决方案 【免费下载链接】parsec-vdd ✨ Perfect virtual display for game streaming 项目地址: https://gitcode.com/gh_mirrors/pa/parsec-vdd 你是否曾为远程服务器缺…...

R语言生态学入门:用rgbif包5分钟搞定GBIF物种分布数据下载(以十大功劳属为例)

R语言生态学入门:用rgbif包5分钟搞定GBIF物种分布数据下载(以十大功劳属为例) 当你在生态学研究中需要快速获取某个物种的全球分布数据时,GBIF(全球生物多样性信息网络)无疑是最权威的数据源之一。但对于刚…...

HTTP基础教程:请求方法、状态码、JSON、鉴权、超时、重试与流式返回

title: “HTTP基础教程:请求方法、状态码、JSON、鉴权、超时、重试与流式返回” date: 2026-04-28 tags: HTTPPythonAPIJSONFastAPIrequests description: “一篇面向初学者的 HTTP 基础博客教程,系统介绍请求方法、状态码、JSON、鉴权、超时、重试和流式…...

DeepAgents智能体

DeepAgents是LangChain 官方发布的 Agent 框架,基于 LangChain LangGraph 构建, 灵感直接来源于 Claude Code——官方 README 里明确写道, 这个项目"最初很大程度上是一次尝试,探究是什么让 Claude Code 如此通用&#xff0…...

如何轻松地将短信从 OnePlus 传输到 iPhone?

从一加这样的Android设备换 到 iPhone固然令人兴奋,但重要的短信怎么办呢?许多用户担心在换机过程中丢失短信历史记录。好在有几种方法可以让你安全高效地将短信从一加转移到 iPhone。本指南将引导你了解一些行之有效的解决方案。第 1 部分。如何通过移动…...

Arm Cortex-A720处理器错误分析与解决方案

1. Arm Cortex-A720处理器错误概述在处理器设计领域,硬件错误(Errata)是每个芯片开发者都需要面对的挑战。Arm Cortex-A720作为高性能计算的核心组件,其设计复杂度带来了某些特定场景下的异常行为。这些错误并非设计缺陷&#xff…...

榨干GD32F470性能:巧用SDRAM+SPI DMA,实现240x280 TFT屏的60FPS流畅动画

榨干GD32F470性能:SDRAMSPI DMA驱动TFT屏的60FPS优化实战 当你在嵌入式系统中需要实现流畅的UI动画时,内存带宽和处理器性能往往成为瓶颈。GD32F470这颗Cortex-M4内核的MCU,配合外置SDRAM和SPI DMA,却能突破内部RAM限制&#xff0…...

告别爆显存!实测Stable Diffusion v1-4模型在低配GPU上的最小化运行参数指南

低配GPU玩转Stable Diffusion:4GB显存极限优化实战手册 当我在自己的旧笔记本上第一次尝试运行Stable Diffusion时,那个刺眼的"CUDA out of memory"错误提示几乎浇灭了我的热情。但经过两周的反复试验和参数调整,我成功让这个拥有4…...

智能运维+多模型服务能力,阿里云 RDS AI 助手旗舰版正式上线!

数据库运维团队常常面临两大难题:一是混杂在阿里云、自建和他云上的各类数据库难以统一管理;二是想利用大模型能力提升运维效率,却要分别对接多个厂商的 API、管理多套密钥、承担高昂的集成成本。 RDS AI 助手旗舰版在 RDS AI 助手专业版智能…...

从CAN波特率索引表到寄存器:一份给嵌入式新手的底层配置原理图解

从CAN波特率索引表到寄存器:嵌入式开发的底层配置逻辑拆解 刚接触CAN总线的开发者,面对波特率配置时往往会遇到一个困惑:为什么有些开发板直接给出一张索引值对照表,而有些手册却要求手动配置7个寄存器?这两种方式背后…...

如何用MusicFree插件系统打破音乐平台壁垒:完整免费音乐聚合指南

如何用MusicFree插件系统打破音乐平台壁垒:完整免费音乐聚合指南 【免费下载链接】MusicFreePlugins MusicFree播放插件 项目地址: https://gitcode.com/gh_mirrors/mu/MusicFreePlugins 你是否厌倦了在不同音乐平台间来回切换?是否因为会员限制而…...

【Docker WASM边缘部署终极指南】:20年架构师亲授3大避坑法则、4层架构图与实时性能调优参数

更多请点击: https://intelliparadigm.com 第一章:Docker WASM边缘部署的演进逻辑与核心价值 WebAssembly(WASM)正从浏览器沙箱走向通用轻量运行时,而 Docker 官方对 WASM 的原生支持(自 2023 年 Docker D…...

本地mysql密码重置

第一步:准备工作关闭所有和 MySQL、DBeaver、CMD 相关的窗口,从头开始。如图:winR打开如下面板,然后确认找到正在运行的mysql服务,然后右键停止。以管理员身份打开 2 个「命令提示符」窗口(右键 CMD → 以管…...

若依(RuoYi-Vue)代码生成器实战:从零掌握单表CURD开发

前言若依框架是国内最流行的Spring Boot后台管理系统之一,其强大的代码生成器可以让我们告别繁琐的增删改查开发,只需几步操作就能生成完整的业务代码。本文将完整记录使用若伊代码生成器完成单表CURD的全流程,并分享实际开发中遇到的各种&qu…...

【LSTM回归预测】基于matlab改进的量子粒子群自适应算法ASL-QPSO优化LSTM循环神经网络的数据回归预测【含Matlab源码 15397期】

💥💥💥💥💥💥💞💞💞💞💞💞💞💞欢迎来到海神之光博客之家💞💞💞&#x1f49…...

别再死记硬背Flink CEP API了!图解‘严格连续’、‘松散连续’到底差在哪?

Flink CEP实战:图解严格连续与松散连续的本质差异 1. 复杂事件处理的核心挑战 在实时数据处理领域,Flink CEP(Complex Event Processing)是检测事件流中特定模式的利器。但许多开发者在实际使用中常陷入一个误区:死记硬…...

【Linux从入门到精通】第27篇:文本处理三剑客(上)——grep 正则表达式实战

目录 一、引言:从“找东西”说起 二、grep基础:从简单搜索开始 2.1 基本语法 2.2 常用基础选项 2.3 管道中的grep 三、正则表达式:从“搜文字”到“搜模式” 3.1 两种正则标准:BRE与ERE 3.2 基础元字符 3.3 扩展正则&…...