明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 1465|回复: 9

[图形系统] 魔改系列-5dwg多线程读写+选择相似功能

[复制链接]
发表于 2024-3-29 18:09:08 | 显示全部楼层 |阅读模式
本帖最后由 你有种再说一遍 于 2024-6-25 05:08 编辑

继上次教大家怎么实现内存布局之后,这次要说一下多线程了,acad数据库虽然是单线程的,但是大部分人好像当圣经一样遵守了...
懒得干和不会干,是两码事哦...
你不会多线程,那和敲lisp的有什么区别呢...


很多人一开始就觉得,为什么不单线程先读取,再构建集合,然后多线程处理数据,答案也很简单,因为嫌慢.


我首先想到用数组类比数据库,什么时候数组不能多线程读取了?只要不改它就是静态资源,肯定是能读的,所以就是读读不冲突,读写冲突,因此我们需要处理脏写和数组扩容问题.
写入的时候,要先阻塞每个读取线程的"下次读",再等待全部读取任务完成,最后执行写入,写入完成再放开读的阻塞...这不妥妥读写锁嘛!只是这样锁的粒度偏大,但是还是非常好用的.毕竟我们不是频插数据,不然还有跳表,B树,B+树,BLink树,BW树...


因为acad有提供句柄总数,所以并行遍历句柄,我们就能提取图元图层等信息.句柄转id的时候,直接id.Open()是可以的,这并不会引起报错,注意,一定不能打开事务.
利用多线程读取面积等于n的多段线,这不爽翻了吗?联想一下我上一篇批量BO边界...


并行分区
c#阻塞类:AutoResetEvent
c#并行类:Parallel.For
c#linq调用AsParallel会实现自动分区,但是前提需要一个集合.而我们的句柄是0...n只能通过多线程或者Parallel.For乱序执行任务,或者ThreadPool线程池,手动分配.
因为句柄总数是巨大的,读线程需要一个游标指示当前读位置(索引i),被写入线程插入之后,能够继续读后面的,而不是重头开始,但是很快你会发现漏读问题.


漏读
因为句柄分配机制不像动态数组一样是末尾自增,它是一种符号表区间非连续的自增.
那么能获取到每个符号表句柄区间范围吗?不能,但是能通过有效范围进行分析?这可能是另一个话题了,还可以自己实现多线程事务...而且cad官方貌似没有处理句柄溢出情况,一旦溢出就会提示你需要修复图纸.
例如读取线程已经读取[0-100],但是只分配到句柄50,写入记录时候就是句柄51了.这样就产生了,你刚刚添加一个面积等于n的多段线,它就没有读到然后漏存.
怎么办怎么办?
答案是,acad提供了数据库事件啊,嘿嘿.我们只需要利用Database图元添加事件/删除事件/撤回事件,在主线程来维护.


重复读
读取游标在句柄50,那么创建是句柄51,主线程读入51,放行读线程也读入51,这需要hashset句柄咯.


写入
写入刚开始就说了,只是需要改变一下阻塞标记,等待信号量放行.然后可以普通地提交.
事务总是主线程写入,它将包裹多条记录一起提交,然后利用事务可以实现回退.写lisp的用户总是喜欢设置undo标记,它本质只是一次性回滚n次提交.


脏写
因为存在:把多段线修改成面积等于n的时候,这个时候我们就在主线程订阅数据库图元修改事件.


在ifox有一个id.Isok方法来判断id是不是能用的,但是它只能用于主线程:
return id is { IsNull: false, IsValid: true, IsErased: false, IsEffectivelyErased: false, IsResident: true}
而在多线程过滤需要移除:IsValid,IsResident.


句柄文章:https://www.cnblogs.com/JJBox/p/12489648.html
(这样就完成了一个粗糙的多线程dwg数据库读写了)


锁的颗粒度
1,锁定每一间房子,每次只允许一个人进去看书.
2,锁定每一本书,每次只允许一个人进去看书.
你会天然觉得2就是效率更高的,因为房间可以一下子充满人.没错,这就是锁的本质,在不少编程语言都有ConcurentHashmap,ConcurentSet这样里面放锁的类型,而且是原子锁CAS,直接利用硬件锁,拉满性能.
而cad要怎么细化这些锁呢?
下次再告诉你.


读写分离
当你读完上面操作,你会发现,感觉事务是写的时候才需要开启的?
没错,因为读写分离能够避免写锁竞争.
很多人又觉得: cad里面开事务读取好方便啊.那是因为你被cad带坏了,因为cad没有并发,令你长期代入一种"自以为是"的感觉.现在我把并发引入进来了,然后是不是感觉效率提高了?
但是我还是感觉有时候需要读取开事务呢?
那是因为你还没有把"自己很菜"加入进来一起思考,举个例子,如果你能不生成文字直接计算文字包围盒,那么是不是就读写分离了?
当然了,我并不知道生成一个和cad渲染一样的包围盒,它没有提供API,万一它又想提供了呢?万一提供的又和显示不一样呢?所以这只是取舍,这不是原理,因为你做不到而不去思考原理,这才是最笨的,你要自己去做一个cad呢?




什么叫必须?
要告诉你操作系统其实不是必须的,那你肯定要说操作系统真好用...单片机还经常跑裸机呢,我们需要底层原理才能优化,OK?你还在写业务,我已经在考虑性能了,OK?


深入一步,去了解一下其他的数据库.
单线程的数据库代表那就是redis了,它的事务压根没有提供回滚,所以根本没有人用它事务.
那么说明回滚也不是必须的,而原子性却是必须的,没错它们概念分离,虽然事务常常包裹原子性.
redis的修改: 先取再判再改,错误回改.相当于手动模拟了一个回滚,而是这必须是原子性才行lua实现,取和改之间不允许插入其他线程任务,而且有时候需要同时修改几个key.错误总是在编码阶段发现,因此debug时候才需要的.
它的好处是什么?它具备更少信息熵更高的性能.而且如果引入MVCC需要缓存事务消息,并不能节约内存.
多线程才需要考虑事务,参考InnoDB的MVCC,
事务1修改了a=1,还在忙.....
事务2修改了a=2,提交事务2,触发约束冲突,事务回滚.
事务1提交.


选择相似
acad高版本有选择相似命令SelectsLmilar,那么怎么在低版本制造一个相似选择...并组块全图替换.
按图元类型名称索引:
1,初始化,通过并行化乱序读入cad句柄,然后类型名作为key存入ConcurentDictionary.
2,通过Database图元添加事件/删除事件/撤回事件,在主线程来维护索引.
3,索引value需要排序树(不按照包围盒排序,因为四叉树单独做)


用户是选择多个相似的图元和图块,通过获取选区每个图元包围盒求交并比就能提取范围了.然后获取相似堆索引中的图元,求出交并比组块和替换.
因此我们需要一个结构记录相似度,以及相似堆.
struct{
  点集数 pts: Points.Lenth
  相似堆 ids: List<objectid>
  对比数组 distance: float[] 使用距离数组,处理信息熵冗余前缀表?凸度呢
  相似度:int 123,-123//镜像相似度*-1.
}
在构建索引时候,遍历一次数据库,加入多段线先判断点集数,无相同点集数就先独立一个加入,在第一次比较时才创建对比数组.
创建对比数组: (1/头尾点距离)*每个前后距离=归一化距离,强转为float.因为溢出也是相同溢出错误,所以能够满足对比.
加入堆:容差比较距离数组,一样的加入同一个堆.
因为存在点序不同,以及环形点序,满足全部要计数.
获取a线第一个距离位于比较数组第几个位置,可能是多个距离相同n,nn,nnn....
n相同时,比较n+1,n+1不等,就下一个nn....循环...直到n全部不满足时候,反转数组再比较n和n+1.
如果n+1相等,n+2不等,那么还是滑动到下一个nn...循环...


为什么是距离数组?
因为距离数组不受逆向影响,只受同向点序影响.
如果是角度数组:每段和x轴的夹角,夹角还可以直接加头尾旋转角用来快速计算.但是角度数组受点序影响很大.例如多段线模拟圆,点序不同时候要线性滑动对比,这个最浪费时间...w左起或者右起,夹角是0-360°难以通过正负或者同方向顺逆时针对比,只能比较两次.w被旋转了呢?所以是错误做法.


Q&A:
自定义图元是僵尸实体?
僵尸实体自己一个集合.


块中
由于句柄遍历的方式是不用考虑嵌套的,我觉得进入块内替换也是很不错,可以提供关键字让用户决定是否替换.同样的,如果选择相似夹着图层锁定,也需要弹提示.
词典中,它可以保存虚拟图元,此时索引上面是存在它的,那么功能也应该过滤掉它.


如何防止没有别人没有插件修改图元造成索引失效?直接多线程重构索引,不用序列化保存索引啦.


DBPoint/XLine/Ray只有一个点数据,岂不是1位置具备大量图元,存在数据倾斜?
由于在不同的类型下实现排序方式不同,此类可以根据点排序,所以他们只是类型下聚合.


以上功能都即将在ifox上面提供,但是鉴于本人失业了,而且电脑已经损坏,遥遥无期,能看懂的自己敲敲吧.
发表于 2024-6-17 11:08:16 | 显示全部楼层
除了考虑写,还要考虑内存申请的问题,这里面的坑也是不少,还不如直接ODA,那才是真的多线程支持
回复 支持 1 反对 0

使用道具 举报

发表于 2024-3-29 18:46:52 | 显示全部楼层
我是属于不会干 的
发表于 2024-3-29 19:07:44 | 显示全部楼层
我就看懂了第一句话,你们呢
发表于 2024-3-29 20:45:33 | 显示全部楼层
好熟悉的头像,惊惊大佬吗

点评

不然呢  发表于 2024-3-29 20:57
发表于 2024-4-1 11:24:40 | 显示全部楼层
Mai1993 发表于 2024-3-29 20:45
好熟悉的头像,惊惊大佬吗

大胆,这么明显的,还要问!
发表于 2024-6-17 22:24:28 来自手机 | 显示全部楼层
我是一句都没看懂  虽然我想懂
 楼主| 发表于 2024-6-19 18:21:12 | 显示全部楼层
walker2010cs 发表于 2024-6-17 11:08
除了考虑写,还要考虑内存申请的问题,这里面的坑也是不少,还不如直接ODA,那才是真的多线程支持

ODA不是谁都能用的,ODA本身也有动态块版权等问题,所以自己动手丰衣足食
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-11-25 04:53 , Processed in 0.191914 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表