实战Moltbot(Clawdbot)教程--使用QQ、钉钉和飞书

核心内容摘要

豆瓣电影推荐系统 | Python Django 协同过滤 Echarts 打造可视化推荐平台 深度学习 毕业设计源码
自定义浏览新体验:ScriptCat脚本工具3步上手指南

Clang Static Analyzer Checker快速上手

嵌入式C教程——引用计数的实现与性能写引用计数的文章就像在讲“谁送快递给谁付钱”——每多一个人拿着快递你的账本上就多记一笔最后那个人把快递丢在门口账本清零快递就可以销毁。

区别是在嵌入式世界里这个“账本”得很小心地放在口袋里不能丢、不能被并发多人改错账还要尽量别让 CPU 为了记一笔账掉进长时间的排队。

什么是引用计数引用计数reference counting就是给对象装个小计数器每当有人“拿走”一份引用就放回去就--当计数变成 0表明没人需要了就销毁对象。

简单、直观、无需全局垃圾回收。

但要注意两个对象互相持有引用——那就是经典的“情侣互相缠着不放cycle”永远不会变成 0需要我们手动拆或用弱引用weak reference。

两种常见实现思路不列长清单用段落说明非侵入式non-intrusive像std::shared_ptr计数器放在对象外面的控制块control block。

优点是对象类不需要修改适配性高缺点是需要额外分配可能两次分配对象 control block这在内存受限或不允许堆分配的嵌入式系统里可能是问题。

侵入式intrusive把计数器放到对象内部通常是基类成员。

优点是零额外分配节省内存与碎片性能更好少一次内存访问缺点是对象类必须侵入式继承或提供接口侵入性高但在自研系统里通常可接受。

对于嵌入式我的倾向是能侵入就侵入不能改类结构的才用非侵入。

单线程、最小化实现嵌入式友好、无锁、无异常如果你是在裸机、单核、没有 preemptive threading 的环境引用计数可以做到非常轻量用uint16_t/uint32_t普通 /-- 就好。

示例// IntrusiveRef.h - 单线程版本适合裸机或只在主任务中使用classIntrusiveRefBase{protected:uint16_tref_count_{1};// 默认持有者为创建者virtual~IntrusiveRefBase()default;public:voidretain()noexcept{ref_count_;}voidrelease()noexcept{if(--ref_count_

deletethis;}};templatetypenameTclassIntrusivePtr{T*p_;public:IntrusivePtr(T*pnullptr):p_(p){}IntrusivePtr(constIntrusivePtro):p_(o.p_){if(p_)p_-retain();}IntrusivePtroperator(constIntrusivePtro){if(o.p_)o.p_-retain();if(p_)p_-release();p_o.p_;return*this;}~IntrusivePtr(){if(p_)p_-release();}T*get()constnoexcept{returnp_;}Toperator*()constnoexcept{return*p_;}T*operator-()constnoexcept{returnp_;}};说明没有std::atomic极简开销适合没有并发的嵌入式场景。

多线程 / 中断上下文下的线程安全引用计数当引用可以在不同任务/中断里操作时你需要原子性。

标准做法是在计数器上使用std::atomicuint32_t。

对性能和正确性的一个典型要求是增加引用retain可以使用比较宽松relaxed内存序但在释放到 0 并执行删除时必须使用 acquire/release 来保证可见性对象构造后的写对删除线程可见。

常见安全模式并被广泛使用// Control block style (non-intrusive) 线程安全示例简化structControlBlock{std::atomicuint32_tref{1};// 可扩展弱引用计数、自定义删除器等};templatetypenameTclassSharedPtr{T*ptr_;ControlBlock*cb_;public:SharedPtr(T*pnullptr):ptr_(p),cb_(p?newControlBlock{}:nullptr){}SharedPtr(constSharedPtro):ptr_(o.ptr_),cb_(o.cb_){if(cb_)cb_-ref.fetch_add(1,std::memory_order_relaxed);}SharedPtroperator(constSharedPtro){if(o.cb_)o.cb_-ref.fetch_add(1,std::memory_order_relaxed);release();ptr_o.ptr_;cb_o.cb_;return*this;}~SharedPtr(){release();}private:voidrelease(){if(!cb_)return;if(cb_-ref.fetch_sub(1,std::memory_order_acq_rel)

{std::atomic_thread_fence(std::memory_order_acquire);deleteptr_;deletecb_;}}};关键点解释fetch_add(1, relaxed)用于提高并发下的吞吐因为仅递增不用保证内存同步fetch_sub(1, acq_rel)用来在最后一个引用离开时以acq_rel保证前面的写入对删除线程可见紧接着的atomic_thread_fence(acquire)确保在删除对象前已同步好所有内存状态这是std::shared_ptr实现中的常见模式。

性能陷阱真正决定工程成败的地方在嵌入式中引用计数的成本不仅是“一次 或 --”而是下面这些常被忽视的问题原子操作的成本在多核系统上原子写会导致缓存行在核心间跳动cache line bouncing这在高频短寿命对象上极昂贵。

单核系统上atomic 也可能被实现为普通指令但在有中断的环境下仍需小心。

伪共享 / 对齐把计数器与其它经常被访问的成员放在同一缓存行会导致无谓的同步开销。

把计数器独立对齐到缓存行边界可以显著提升并发性能代价是更多内存。

控制块的额外跳转非侵入式需要额外一次堆读取control block每次拷贝/赋值都可能触发内存访问影响速度和能耗。

短寿命对象竞争如果对象在多个线程中频繁 acquire/release原子竞争会成为瓶颈。

此类场景下考虑对象池reuse或尽量限制共享的粒度。

中断与锁在 ISR 中修改计数器时若不能使用 atomics或 atomics 不可用可能需要在临界区禁中断这会影响中断延迟。

简而言之如果你的对象经常在多核、多任务间短时间内大量拷贝引用引用计数会吃掉 CPU 时间如果对象相对长寿比如资源句柄引用计数的开销通常是可以接受的。

嵌入式实践建议如果你在做嵌入式 C可以按下面的优先级来选择实现策略先判断是否有并发与中断场景。

如果没有并发使用侵入式非原子计数最省。

若有并发但系统是单核且可在关键段禁中断把计数器的修改放在关中断的临界区里成本比全原子低适合对延迟敏感但只有少量竞争的场景。

对真正的多核并发使用std::atomic的实现尽量使用侵入式以减少内存访问如果不能侵入并且堆分配昂贵考虑对象池或预分配 control-block 的策略。

此外要避免循环引用在父子关系里父持有子强引用子持父弱引用或者在文档中明确约定谁负责生命周期simplicity beats cleverness。

简单性能微基准建议不需要复杂工具写个基准程序测两件事单线程下每次拷贝/销毁耗时测newsharedptrvsintrusive以及 N 线程同时频繁拷贝/释放的吞吐。

测量使用std::chrono::steady_clock而且要注意热身和大量重复百万级以消除计时误差。

注意记录 CPU 占用和功耗若设备可测。

往深里走补充注意点如果你要把weak_ptr做到位需要维护第二个弱引用计数且在最后一个强引用消失时不要删除 control block 直到弱引用也为 0。

这会增加复杂度但解决循环引用是必要的。

还有一种替代方案是观察者observer手动释放以及明确的生命周期层级owner/borrower策略在嵌入式中明确的所有权往往比自动垃圾更可靠。

结语在嵌入式里引用计数既是救星也是麻烦引用计数给我们提供了容易理解的资源管理模型但在资源受限、高并发、实时性敏感的嵌入式世界里它也需要被谨慎使用。

我的经验是在短任务、低并发场景下用侵入式的简单实现在多核复杂场景下用原子并结合对象池或上下文限定减少全局共享永远别忘了防环weak/refactor和测量measure, measure, measure。

写好单元测试和压力测试让你的引用计数在真实负载下过关。

ycit.91job.gov.cn-ycit.91job.gov.cn最新版v.7.60.02-2285安卓网应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123