C#当中,连续的耗时操作,如何在另外一个线程中进行呢

数码科技
2022-05-12 14:32:41
40
504
此间风物
Lv.15 内涵丰富的锡龙

各位大佬好,请问一下,假设有这样一个真实应用场景:

对文档进行编辑,每分钟200+的高速打字过程中,连续触发保存条件
如果前一个保存操作未完成,又来了新的保存操作,则前一个任务取消,执行最新保存任务

本质上,还是想要实现一种用户完全无感的实时保存操作

如何用比较优雅的方式去实现呢?

环境:sqlite数据库操作,C#+WPF,多线程

由于sqlite在本地进行IO操作,可能存在轻微的延时,造成卡顿,决定在多线程中实行。

我自己试验了几种方法,在低速打字状态都工作得很好,但测试速度快到一定程度就总是各种莫名其妙的bug,应该是逻辑不正确,但在低速状态靠电脑性能硬吃了。

如果靠着系统api,检查空闲……这样的逻辑会更好吗?

回复
评分
收藏
举报
#(滑稽)
Lv.16 千锤百炼的铁龙

建议活用队列,UI thread 把用户输入推送到队列中,IO thread 读取队列中的数据并依次写入文件。一个标准的生产者-消费者模型。

另,如果要从文件回读数据的话,建议先锁定用户操作,等待队列清空后再回读。

1条评分,共1龙币收起评论
此间风物1
回复
评分
收藏
举报
弗洛伊德朱
Lv.1 正在孵化的龙蛋

async task, await

1条评分,共1龙币收起评论
此间风物1
回复
评分
收藏
举报
嫌疑犯罪分子
Lv.6 青春洋溢的萌龙
弗洛伊德朱async task, await

正想说async大法....

回复
评分
收藏
举报
烛火
Lv.19 爱与正义的金龙

你不能全量保存啊, 每次只保存增量部分. (但这超复杂, 数据结构这一块很难搞定, 退一步你还是全量保存吧)

然后增量部分写入一个队列, 有一个线程专门从队列里往磁盘写入.

程序意外结束的话未写入磁盘部分的队列内容会丢失.

回复
评分
收藏
举报
此间风物
Lv.15 内涵丰富的锡龙
烛火你不能全量保存啊, 每次只保存增量部分. (但这超复杂, 数据结构这一块很难搞定, 退一步你还是全量保存吧)

就是sqlite这里不知道怎么操作……只能傻乎乎的全量保存了。其实我也感觉这样很不好,字数多了就会出现各种各样的麻烦

倒是实时字数统计,我实现了只计算一次原始文档,然后追踪变化……因为是在软件内部进行操作,所以相对而言容易实现。

我用的数据库是sqlite,本地单独一个.db文件的数据库,请问大佬你知道这个怎么实现吗?

数据结构这一块反而不用担心,我能够追踪文字变化!包括InsertedText和RemovedText!

回复
评分
收藏
举报
此间风物
Lv.15 内涵丰富的锡龙
烛火你不能全量保存啊, 每次只保存增量部分. (但这超复杂, 数据结构这一块很难搞定, 退一步你还是全量保存吧)

实在不行的话,我就用时间间隔来作为判断条件算了。

这个相对而言比较简单,每隔一秒保存一次(但总感觉还是有些不太对劲?)

回复
评分
收藏
举报
#(滑稽)
Lv.16 千锤百炼的铁龙

你的业务逻辑是咋设计的?每敲一个字全量保存一次数据?

原则上我不建议文本编辑器用这样的设计形式,一是效率太差,二是也很浪费硬盘空间,一章3000字需要至少10MB存历史记录,而如果往10000字写则一章就要100MB了。

有几种简单的建议:

1. 你自己说了,每秒保存一次,不过记的加上变更检查,有变更了才保存,别作者啥都不写还一个劲保存数据。

2. 每“单元”保存一次,而不是每个字保存一次。“单元”的定义可以有很多,最简单的定义就是以标点符号为间隔,每输入一个标点符号就触发一次保存操作。这样实现难度不高,保存密度也还尚可接受。

3. 实现难度稍大的,把章节为单位的存储结构改为段落。这样你每个段落每个字都触发保存,整体体量也是可以接受的。但是你需要相应的实现段落、章节的拆分与合并逻辑,考虑到你可能针对章节做了很多辅助功能,因此对程序的影响较大。

回复
评分
收藏
举报
此间风物
Lv.15 内涵丰富的锡龙
#(滑稽)[表情]你的业务逻辑是咋设计的?每敲一个字全量保存一次数据?

其实最初是想要实现类似OneNote那样的保存方式的。

我也知道不能每一次变动都全量保存

我采用你说的第二条,特定条件,比如换行,逗号,句号则保存。(就是这里,低速输入状态还可以接受,但是考虑到非常态的飚手速,可能存在一些问题)

目前看来,最简单的应该还是按照时间间隔自动保存。这个肯定要判断是否发生了变动的……

之前说的,针对章节进行的很多操作,比如说实时的关键词刷新,前后多个附近章节的联动等等,都在单独线程当中实现了,反而不影响了。字数统计更是只统计变更的部分,就算几亿字的大文件也能完美流畅的更新。

但在程序之外,数据库sqlite的读写操作,似乎比普通文本文档的IO开销更大一些。

如果需要涉及到一些针对网络同步,解锁/锁定数据库……耗时就更大了

我考虑了一下,卡顿就是从这里来。

如果只是简单的文本文档保存,其实小文件下,即便不用多线程,也完全可以实现我的需求的。

回复
评分
收藏
举报
#(滑稽)
Lv.16 千锤百炼的铁龙
此间风物其实最初是想要实现类似OneNote那样的保存方式的。

如果你了解过 sqlite 的写流程的话,想必也就能了解为什么 sqlite 写开销比文件 IO 开销大了。尤其是你可能还要针对文本内容建立索引,那开销就更大了。

通过独立出 IO 线程可以从形式上解决问题,但是要解决根本问题的话我的建议还是修改存储结构,减轻每次写入的压力。

回复
评分
收藏
举报
此间风物
Lv.15 内涵丰富的锡龙
#(滑稽)[表情]你的业务逻辑是咋设计的?每敲一个字全量保存一次数据?

之前考虑过外挂git来存历史记录,后来感觉没有必要这么麻烦,采用了坚果云的同步方案,把网络同步解决了。还有历史记录。当然,这个历史记录要调出来也比较麻烦……但多多少少是个备份

回复
评分
收藏
举报
#(滑稽)
Lv.16 千锤百炼的铁龙
此间风物之前考虑过外挂git来存历史记录,后来感觉没有必要这么麻烦,采用了坚果云的同步方案,把网络同步解决了。还有历史记录。当然,这个历史记录要调出来也比较麻烦……但多多少少是个备份

外挂git是真的没必要。。。闲得没事一直 commit 也是个很扯淡的事情

回复
评分
收藏
举报
此间风物
Lv.15 内涵丰富的锡龙
#(滑稽)如果你了解过 sqlite 的写流程的话,想必也就能了解为什么 sqlite 写开销比文件 IO 开销大了。尤其是你可能还要针对文本内容建立索引,那开销就更大了。

嗯,多谢你的建议,目前看来是只能从形式上来解决了。比如说空闲时间操作,可能会加大吞字的风险,但实际使用应该还是很少发生的……

我思路上可能走了一些弯路

如果是在空闲时间才进行一些判断,那么用户同样能够“无感”

这里甚至不必用多线程……省得一些弯弯绕绕的东西

挠头,终于明白为什么程序员要开会了。

回复
评分
收藏
举报
此间风物
Lv.15 内涵丰富的锡龙
#(滑稽)[表情]外挂git是真的没必要。。。闲得没事一直 commit 也是个很扯淡的事情

有时候能够偷懒解决的也想要偷懒一下的,历史记录功能,的确很有必要,但要做这个功能,就难免多出一些模块,至少几百上近千行代码,还有可能产生相互覆盖的bug。

干脆用一些能够自动解决的办法。

后来我发现了一个好东西,坚果云,一款同步云盘产品。

回复
评分
收藏
举报
此间风物
Lv.15 内涵丰富的锡龙
#(滑稽)如果你了解过 sqlite 的写流程的话,想必也就能了解为什么 sqlite 写开销比文件 IO 开销大了。尤其是你可能还要针对文本内容建立索引,那开销就更大了。

这个还真没有了解过。

我之所以采用sqlite来作为这款软件的数据库,完全是因为机缘巧合。

现在想想看……其实利用文件数据对象持久化来保存数据也行的。

单独文件,一个章节8~12kb……利用GUID作为文件名,可以避免很多重名覆盖之类的风险

然后开发一个管理模块,在软件内部对文件进行管理就行了……

唉,这里的软件用数据库有利有弊啊

回复
评分
收藏
举报
烛火
Lv.19 爱与正义的金龙

做文本编辑器是个大活, 你不可能一直在最后面往下写, 回过头去删一段, 把某一段剪切了插入到前面去等等操作会让你的数据结构变很复杂, 然后你至少还得支持^Z和^Y, 这一加上撤销和恢复就更复杂了, 全量保存简单没这些问题, 但全量保存不是个正路.

想要支持大文件还得考虑仅加载当前视口, 页面滚动的流畅性等等.

2条评分,共2龙币收起评论
此间风物1
此间风物1
回复
评分
收藏
举报
细思恐极
Lv.10 善解人意的绿龙
此间风物这个还真没有了解过。

考虑 nosql ?

1条评分,共1龙币收起评论
此间风物1
回复
评分
收藏
举报
此间风物
Lv.15 内涵丰富的锡龙
细思恐极考虑 nosql ?

数据库结构不可能大改了,改了的话几乎等于重做,还要加上学习时间和重制时间的成本,不实际。

最初只是作为一个写手,对市面上现存的软件不甚满意,本着我辈中人“我行我上”的优良传统,不服就干,硬顶着做了一个软件出来,做到这地步已经是尽力了。

等我开完新书,验证好这个软件再考虑开源,算是抛砖引玉吧

也找不到什么门路卖钱……到时候再看看

回复
评分
收藏
举报
此间风物
Lv.15 内涵丰富的锡龙
烛火做文本编辑器是个大活, 你不可能一直在最后面往下写, 回过头去删一段, 把某一段剪切了插入到前面去等等操作会让你的数据结构变很复杂, 然后你至少还得支持^Z和^Y, 这一加上撤销和恢复就更复杂了, 全量保存简单没这些问题, 但全量保存不是个正路.

是的啊,不过我采用了一个框架来偷懒。

应该是pieceTable的结构。

其实它甚至可能包含了流式文件读取和增量保存的,但问题在于……只支持文本文档(好像),如果想要保存在数据库中,配合打包上传,还是会很麻烦的。

我正在尝试把这些东西分开。

回复
评分
收藏
举报
此间风物
Lv.15 内涵丰富的锡龙
烛火做文本编辑器是个大活, 你不可能一直在最后面往下写, 回过头去删一段, 把某一段剪切了插入到前面去等等操作会让你的数据结构变很复杂, 然后你至少还得支持^Z和^Y, 这一加上撤销和恢复就更复杂了, 全量保存简单没这些问题, 但全量保存不是个正路.

我直接采用框架来实现你说的这些基本功能,在外面套了一层壳,然后挂在TabItem的页面当中,实现了多标签多文档编辑……

精力主要集中在其他方面,比如说关键词着色方案,挂接角色数据库,敏感词判断等等,还有基于树节点的一些处理……

目前在各位大佬的建议下改换了保存的逻辑,虽然个人感觉还是不够优雅,但的确解决了表面呈现的一些问题,是否绕了弯路,是否怎样,我都懒得管了,正在手动进行各种粗暴乱来的黑盒测试(我是业余的,没有白盒测试能力,甚至都不会写单元测试)

目前看来,马马虎虎(暂未发现网络波动下的及时同步问题……)

回复
评分
收藏
举报

本帖需登录才可回复,没有帐号欢迎立即注册