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

Golang并发安全泛型集合(Set)设计与实现

并发安全泛型集合(core/pkg/set)设计与实现

1. 简介

set包提供CSet[T comparable]:在 Go 泛型下实现的线程安全集合(元素不重复)。底层使用map[T]struct{}作为紧凑存储(空 struct 不占值内存),对外通过sync.RWMutex隔离并发读写。

VSS项目应用:流名去重、会话 key 占用标记、邀请/拉流防并发击穿、设备侧状态去重等的场景。


2. 设计要点

2.1 为何不导出底层 map 类型

底层setMap[T]不导出,仅CSet[T]暴露 API,避免绕过锁直接改map的误用。

2.2Range:快照遍历与死锁规避

若在RWMutex读锁持有期间执行用户回调f,而f内部再次调用同一CSetAdd/Remove(写锁),会典型地读锁未释放又等写锁,在标准RWMutex上容易死锁

因此Range实现为

  1. 短临界区:在锁内仅复制当前键列表(snapshotKeys);
  2. 锁外枚举:对快照逐元素调用f

2.3Values与快照

Values()snapshotKeys一致:返回当前时刻所有元素的一份新切片顺序未定义(与map遍历顺序一致)。适合一次性打印或调试,不适合依赖稳定排序(若需要排序请调用方slices.Sort

2.4 容量提示New(hint uint)

Newhint传给make(map, hint),仅为减少扩容次数不是集合大小上限。高并发写热点集合时可适当增大 hint。

2.5nil接收者

(*CSet[T])(nil)调用Add/Remove/Clear/Range等为空操作(不 panic);Contains为 false,IsEmpty为 true,Size为 0,Values为 nil,与常见「防御性」用法一致。

2.6 批量Add/Remove

  • Add(elements ...T):多参数等价于依次加入,单次持锁完成,减少锁次数。
  • Remove(elements ...T):批量删除。

3. API 一览

方法说明
New[T](hint uint) *CSet[T]初始化构造
Add(elements ...T)并入集合
Remove(elements ...T)删除
Clear()清空
Contains(T) bool是否包含
IsEmpty() bool是否为空
Size() int元素个数
Range(func(T) bool)快照遍历,false提前结束
Values() []T快照切片

4. 在项目中的典型用途(VSS)

ServiceContext中示例:

  • PubStreamExistsState:记录已存在推流名,避免重复占流;
  • InviteRequestState:同一streamName防击穿;
  • FetchDeviceVideoState/TalkSipSendStatus:设备或会话的进行中标记。

这类场景共同特点:高并发读Contains、短路径写Add/RemoveRWMutex较互斥锁更友好。


5. 与其它容器选型

需求更适合
键值对、按 key 取 valuexmap/sync.Map/ 业务 map+锁
只关心成员与否、去重CSet
读多写少、key 为 string可考虑sync.Map

6. 并发与性能提示

  • 读多写少时:Contains/Size/IsEmpty走读锁,可并行。
  • Range/Values会与写操作抢占锁复制快照;快照长度大时复制成本与O(n)内存分配相关,避免在超大集合上高频调用。
  • 基准测试:go test ./core/pkg/set/... -bench=. -benchmem

7. 总结

core/pkg/set小 API 面 + 不导出底层 map + 快照式Range,在保持并发安全的同时,
规避绕过锁Range回调重入死锁两类常见问题;
配合New批量 Add/Remove,适合在信令与媒体网关中做轻量级集合状态。

http://www.jsqmd.com/news/577985/

相关文章:

  • 保姆级教程:在GD32F103上用Keil MDK5和FreeRTOS 202411.00创建你的第一个多任务LED闪烁项目
  • 从CVE-2018-15473看协议安全:一个数据包畸形引发的OpenSSH‘侧信道’故事
  • 基于联合概率数据关联滤波器(JPDA)的Matlab代码:实时绘制目标与杂波的动态跟踪与RMS...
  • LVGL缓冲区机制深度解析:从源码看性能优化与场景适配
  • 新手避坑指南:Verilog批量例化模块时容易忽略的3个细节(含波形调试演示)
  • 3大场景攻克视频监控难题:WVP-GB28181-Pro开源解决方案实战指南
  • 别再用requests库硬爬了!Python新手必看的robots.txt检查与BeautifulSoup实战避坑指南
  • 遥感小白看过来!无需编程5分钟搞定Landsat8数据下载(2023最新版)
  • 突破模拟器限制的APK直装方案:Windows系统的Android应用无缝运行技术
  • 新手福音:用快马平台零代码基础生成产区标准对比网页
  • 避坑指南:基于ESP-ADF开发多功能播放器,SD卡音频、蓝牙音箱与语音唤醒的实战配置
  • 实战指南:基于快马平台与openclaw+ollama打造可部署的智能识图应用
  • 合宙ESP32 C3搭配0.96寸LCD屏的完整开发指南(附接线图与库安装)
  • 第2篇:嵌入式芯片发展历程与全球主流厂商产品线全梳理
  • 英飞凌TC3xx SOTA实战:手把手教你配置SWAP功能,实现汽车ECU空中升级
  • 计算机毕业设计springboot在线游戏平台基于SpringBoot的数字化游戏资源聚合与玩家互动社区 SpringBoot框架下的网络游戏资讯分发与玩家服务门户
  • Attu:革新向量数据库管理的可视化工具
  • Ubuntu 24.04 主机名修改全攻略:从基础到自动化脚本
  • PLECS BUCK电路PI调参实战:穿越频率选600Hz还是100Hz?一个仿真对比讲清楚响应速度与稳定性的权衡
  • C++构造函数的引入
  • Golang实战:利用serial包实现跨平台串口通信
  • Jetson Orin NX开机自动跑YOLO+ROS?一个脚本搞定所有终端启动(附环境激活避坑点)
  • 保姆级教程:Windows 11下用QPST工具为红魔8S Pro+进行9008深度刷机(附驱动问题解决方案)
  • 毫米波雷达数据处理避坑指南:AWR2243的complex1x与complex2x格式到底怎么选?
  • TX12 + ExpressLRS 915MHz RC链路优化与EdgeTX固件升级实战
  • 白转黑哪个养发机构更专业?黑奥秘20年深耕,超200万用户见证,效果可视化 - 美业信息观察
  • 论文写作与投稿指南:如何正确引用IEEE TIP、TMI等期刊会议名称(附Latex/BibTeX模板)
  • 原来好写作AI是毕业论文的“智能地图”,不是“代驾司机”
  • 【实用技巧】-Mac系列设备自定义鼠标指针颜色与动态效果指南
  • 提升部署效率:基于快马平台生成ubuntu服务器无人值守安装与初始化脚本