精易论坛

标题: 固定数组线程安全键取索引 替代小量键值表场景 [打印本页]

作者: 易造轮    时间: 昨天 02:49
标题: 固定数组线程安全键取索引 替代小量键值表场景

必须强调的安全前提


这个方案的线程安全是有严格前提的,破一条就可能出问题:

单写原则:只能有一个线程调用 有序尾插键,多写必须加临界区;

有序原则:新键的字典序必须 ≥ 当前数组最后一个键,例程里的校验建议保留;

不扩容原则:初始化一次性分配足够容量,运行中绝不调用 重定义数组();

顺序原则:写入永远是「先写元素,后更计数」,顺序不能颠倒;


只增不删、严格递增的场景下,这个方案的性能和简洁度都是最优的。



  
窗口程序集名保 留  保 留备 注
窗口程序集_启动窗口   
变量名类 型数组备 注
键数组文本型2000 
有效成员数整数型当前有效元素数量,初始为0
停止测试逻辑型线程停止标记
总cha询次数整数型统计总cha询量
错误次数整数型统计cha询错误次数
统计许可区整数型仅用于统计变量加锁,不影响核心cha询逻辑

子程序名返回值类型公开备 注
__启动窗口_创建完毕  

子程序名返回值类型公开备 注
_按钮1_被单击  
变量名类 型静态数组备 注
i整数型 
' 初始化:预填充1000条有序基础数据
有效成员数 = 0
计次循环首 (1000, i)
键数组 [i]“key_”文本补零 (到文本 (i), 4)
有效成员数 = 有效成员数 + 1
计次循环尾 ()
' 重置测试状态
停止测试 = 假
总cha询次数 = 0
错误次数 = 0
统计许可区 = 创建进入许可证 ()
' 启动8个并发读线程
计次循环首 (8, i)
启动线程 (&读线程工作, , )
计次循环尾 ()
' 启动1个写线程
启动线程 (&写线程工作, , )
' 持续测试10秒
延时 (10000)
停止测试 = 真
延时 (500)  ' 等待所有线程退出
' 输出结果
信息框 (“多线程安全验证完成”#换行符#换行符“总cha询次数:”到文本 (总cha询次数)#换行符“错误次数:”到文本 (错误次数)#换行符“最终有效成员数:”到文本 (有效成员数), 0, “验证结果”, )
子程序名返回值类型公开备 注
读线程工作 
变量名类 型静态数组备 注
目标序号整数型 
目标键文本型 
查到下标整数型 
置随机数种子 ()
判断循环首 (取反 (停止测试))
' 随机生成一个已存在范围内的键
目标序号 = 取随机数 (1, 有效成员数)
目标键 = “key_”文本补零 (到文本 (目标序号), 4)
' 核心:无锁二分cha询
查到下标 = 二分查找下标 (目标键)
' 校验结果正确性
如果真 (查到下标 = -1 键数组 [查到下标] ≠ 目标键)
' 仅统计加锁,不影响核心cha询性能
进入许可区 (统计许可区)
错误次数 = 错误次数 + 1
退出许可区 (统计许可区)
' 统计总cha询量
进入许可区 (统计许可区)
总cha询次数 = 总cha询次数 + 1
退出许可区 (统计许可区)
判断循环尾 ()
子程序名返回值类型公开备 注
写线程工作 
变量名类 型静态数组备 注
i整数型 
新键文本型 
' 从第1001个键开始插入,一直插到第3000个
计次循环首 (2000, i)
如果真 (停止测试)
跳出循环 ()
新键 = “key_”文本补零 (到文本 (1000 + i), 4)
有序尾插键 (新键)
' 1ms插入一个,模拟低频新增,同时给读线程留足并发窗口
延时 (1)
计次循环尾 ()
子程序名返回值类型公开备 注
文本补零文本型 
参数名类 型参考可空数组备 注
原文本文本型
总长度整数型
变量名类 型静态数组备 注
补零数整数型 
补零数 = 总长度 - 取文本长度 (原文本)
如果真 (补零数 ≤ 0)
返回 (原文本)
返回 (取重复文本 (补零数, “0”) + 原文本)
子程序名返回值类型公开备 注
二分查找下标整数型 找到返回下标,未找到返回-1;读线程全程无锁
参数名类 型参考可空数组备 注
目标键文本型
变量名类 型静态数组备 注
整数型 
整数型 
中间整数型 
比较结果整数型 
当前有效数整数型 
' ========== 关键安全细节 ==========
' 只读一次有效成员数到局部变量,本次cha询全程使用该快照
' 避免写入过程中计数变化导致越界,也保证本次cha询范围一致
当前有效数 = 有效成员数
左 = 1
右 = 当前有效数
判断循环首 (左 ≤ 右)
中间 (左 + 右) ÷ 2
比较结果 = 文本比较 (键数组 [中间], 目标键, )  ' 区分大小写按需调整
如果真 (比较结果 = 0)
返回 (中间)
如果真 (比较结果 < 0)
左 = 中间 + 1
如果真 (比较结果 > 0)
右 = 中间 - 1

判断循环尾 ()
返回 (-1)
子程序名返回值类型公开备 注
有序尾插键逻辑型 仅允许单个写线程调用
参数名类 型参考可空数组备 注
新键文本型
变量名类 型静态数组备 注
最后位置整数型 
最后位置 = 有效成员数
' 1. 容量硬校验:超过预分配直接失败,绝不触发扩容
如果真 (最后位置 ≥ 取数组成员数 (键数组))
返回 ()
' 2. 有序性校验:确保新键 ≥ 最后一个键,维持数组有序
如果真 (最后位置 > 0 文本比较 (新键, 键数组 [最后位置], ) < 0)
返回 ()  ' 新键更小,拒绝插入,避免破坏有序性
' ========== 严格执行顺序:先写元素,后更计数 ==========
键数组 [最后位置 + 1] = 新键
有效成员数 = 最后位置 + 1
返回 ()


i支持库列表   支持库注释   
EThread多线程支持库


作者: 易造轮    时间: 昨天 03:27

从性能、稳定性、安全性、灵活性等核心维度,对「有序尾插+无锁二分」和「哈希表+临界区」做完整对比,最后给出明确选型建议。




一、核心指标总览表




















































对比维度 有序尾插 + 无锁二分查找 哈希表 + 临界区
平均读延迟 ~0.1~0.3μs ~0.3~0.8μs
延迟稳定性(P99) 极好,无任何抖动 一般,锁竞争会产生毛刺
写性能(单写) 极致,O(1) 指针赋值 较好,O(1) 但有加锁开销
线程安全强度 前提依赖型安全(约束严格) 强安全(正确加锁则绝对安全)
功能灵活性 极弱,仅支持有序尾插 强,支持任意增删改
内存占用 & 缓存友好度 极低,连续内存,缓存命中率高 中等,桶结构,内存碎片化稍高
实现可控性 完全自研,逻辑透明,易排查 依赖第三方模块,黑盒属性强
维护容错率 低,违反前提即出隐蔽bug 高,通用方案不易踩坑



二、分维度详细对比


1. 读性能:绝对速度差距小,稳定性差距巨大


这是量化场景最核心的差异,两者平均延迟都在微秒级,日常使用感知不到,但延迟抖动(P99/P999)天差地别


有序尾插 + 无锁二分



哈希表 + 临界区




关键结论:1000条规模下,无锁二分的读性能不仅更快,更重要的是延迟完全可预测、无毛刺,这是高频交易系统最看重的特性。



2. 写性能:尾插方案有压倒性优势


有序尾插 + 无锁二分



哈希表 + 临界区



3. 线程安全:一个靠约束,一个靠机制


这是两者最本质的区别:安全的来源完全不同。


有序尾插 + 无锁二分



哈希表 + 临界区



4. 功能灵活性与适用边界


有序尾插 + 无锁二分



哈希表 + 临界区



5. 易语言生态的额外差异





三、最终选型建议


选「有序尾插 + 无锁二分」的场景


如果以下条件你都能长期满足,这个方案是极致最优解:



  1. 能100%保证键永远按字典序递增追加,不会出现乱序插入;

  2. 业务确定只增不删、不改已有键值,后续不会有删除/修改需求;

  3. 只有一个写线程,初始化一次性分配足容量,全程不扩容;

  4. 对延迟稳定性要求极高,不能接受任何锁竞争导致的毛刺。



这是典型的“窄场景极致优化”,符合你的使用规则的话,性能和稳定性都碾压哈希表方案。



选「哈希表 + 临界区」的场景


只要满足以下任意一条,优先选哈希表+临界区,更稳妥:



  1. 无法保证键永远递增,可能需要随机插入中间键;

  2. 后续可能需要删除、修改键值对,功能有不确定性;

  3. 可能存在多个写线程,或者不想被“单写、有序、不扩容”这些规则束缚;

  4. 追求通用、稳妥、低维护成本,不想为了微秒级的优化承担逻辑风险。



作者: wgqxj    时间: 昨天 10:05
谢谢分享
作者: 1184798949    时间: 昨天 10:12
感谢分享
作者: wh1234567    时间: 昨天 10:27
感谢分享
作者: fastdao    时间: 昨天 10:45
感谢分享
作者: 一指温柔    时间: 昨天 11:07
感谢分享
作者: 胖子葛格    时间: 昨天 13:51
感谢大神分享~!
作者: bianyuan456    时间: 昨天 13:55
已经顶贴,感谢您对论坛的支持!
作者: 明天自然醒    时间: 昨天 15:55
我了个小量二分查找,定长+有序锁死了应用场景,终究是不成熟的方案,不如老老实实用哈希表
作者: yxl2008    时间: 昨天 16:43
感谢大神分享~!  
作者: pipicool    时间: 昨天 18:37
学习一下
作者: 夏亿    时间: 昨天 21:52
感谢分享




欢迎光临 精易论坛 (https://bbs.ijingyi.com/) Powered by Discuz! X3.4